From c64ddc4533866930e1eb3191bc92491669456645 Mon Sep 17 00:00:00 2001 From: Bill Nottingham Date: Wed, 25 Nov 2009 23:15:29 +0000 Subject: [PATCH 01/24] Fix typo that causes a failure to update the common directory. (releng #2781) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dad7c56..59ecad3 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ NAME := sepostgresql SPECFILE = $(firstword $(wildcard *.spec)) define find-makefile-common -for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done +for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$d/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done endef MAKEFILE_COMMON := $(shell $(find-makefile-common)) From 478ff226faf94b59e508ba2352f73a71eceadfcc Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Tue, 8 Dec 2009 06:00:14 +0000 Subject: [PATCH 02/24] - rework: backport features from v8.5devel tree - fixbug: selinux netlink receiver process didn't have correct ps display --- sepostgresql-8.4.patch | 8381 ++++++++++++++++++++++++++++------------ sepostgresql.spec | 6 +- 2 files changed, 6012 insertions(+), 2375 deletions(-) diff --git a/sepostgresql-8.4.patch b/sepostgresql-8.4.patch index 4e35401..b703db3 100644 --- a/sepostgresql-8.4.patch +++ b/sepostgresql-8.4.patch @@ -406,7 +406,7 @@ diff -Nrpc base/src/backend/access/common/tupdesc.c sepgsql/src/backend/access/c { diff -Nrpc base/src/backend/access/heap/heapam.c sepgsql/src/backend/access/heap/heapam.c *** base/src/backend/access/heap/heapam.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/access/heap/heapam.c Tue Sep 8 23:55:48 2009 +--- sepgsql/src/backend/access/heap/heapam.c Tue Dec 8 14:53:28 2009 *************** *** 54,59 **** --- 54,60 ---- @@ -591,7 +591,7 @@ diff -Nrpc base/src/backend/access/transam/xact.c sepgsql/src/backend/access/tra diff -Nrpc base/src/backend/bootstrap/bootparse.y sepgsql/src/backend/bootstrap/bootparse.y *** base/src/backend/bootstrap/bootparse.y Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/bootstrap/bootparse.y Wed Jul 15 19:35:52 2009 +--- sepgsql/src/backend/bootstrap/bootparse.y Thu Oct 8 09:29:32 2009 *************** *** 42,47 **** --- 42,48 ---- @@ -604,15 +604,17 @@ diff -Nrpc base/src/backend/bootstrap/bootparse.y sepgsql/src/backend/bootstrap/ #include "storage/ipc.h" *************** Boot_CreateStmt: *** 211,216 **** ---- 212,222 ---- +--- 212,224 ---- else { Oid id; + Oid *secLabels = -+ sepgsqlCreateTableColumns(NULL, -+ LexIDStr($5), -+ PG_CATALOG_NAMESPACE, -+ tupdesc, RELKIND_RELATION); ++ 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, @@ -625,7 +627,7 @@ diff -Nrpc base/src/backend/bootstrap/bootparse.y sepgsql/src/backend/bootstrap/ elog(DEBUG4, "relation created with oid %u", id); } do_end(); ---- 231,238 ---- +--- 233,240 ---- 0, ONCOMMIT_NOOP, (Datum) 0, @@ -636,10 +638,10 @@ diff -Nrpc base/src/backend/bootstrap/bootparse.y sepgsql/src/backend/bootstrap/ do_end(); diff -Nrpc base/src/backend/bootstrap/bootstrap.c sepgsql/src/backend/bootstrap/bootstrap.c *** base/src/backend/bootstrap/bootstrap.c Fri Feb 20 22:15:36 2009 ---- sepgsql/src/backend/bootstrap/bootstrap.c Wed Sep 9 13:14:37 2009 +--- sepgsql/src/backend/bootstrap/bootstrap.c Tue Dec 8 14:04:25 2009 *************** -*** 26,31 **** ---- 26,32 ---- +*** 26,37 **** +--- 26,39 ---- #include "access/xact.h" #include "bootstrap/bootstrap.h" #include "catalog/index.h" @@ -647,9 +649,45 @@ diff -Nrpc base/src/backend/bootstrap/bootstrap.c sepgsql/src/backend/bootstrap/ #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 **** ---- 498,508 ---- +--- 510,520 ---- */ boot_yyparse(); @@ -663,7 +701,7 @@ diff -Nrpc base/src/backend/bootstrap/bootstrap.c sepgsql/src/backend/bootstrap/ CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); *************** InsertOneTuple(Oid objectid) *** 794,799 **** ---- 800,807 ---- +--- 812,819 ---- tupDesc = CreateTupleDesc(numattr, RelationGetForm(boot_reldesc)->relhasoids, attrtypes); @@ -711,7 +749,7 @@ diff -Nrpc base/src/backend/catalog/Makefile sepgsql/src/backend/catalog/Makefil toasting.h indexing.h \ diff -Nrpc base/src/backend/catalog/aclchk.c sepgsql/src/backend/catalog/aclchk.c *** base/src/backend/catalog/aclchk.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/aclchk.c Wed Sep 9 21:29:33 2009 +--- sepgsql/src/backend/catalog/aclchk.c Fri Sep 18 14:51:00 2009 *************** *** 35,40 **** --- 35,41 ---- @@ -747,25 +785,91 @@ diff -Nrpc base/src/backend/catalog/aclchk.c sepgsql/src/backend/catalog/aclchk. /* Views don't have any system columns at all */ if (classForm->relkind == RELKIND_VIEW && curr_att < 0) continue; +*************** ExecGrant_Attribute(InternalGrant *istmt +*** 774,779 **** +--- 782,789 ---- + relOid, grantorId, ACL_KIND_COLUMN, + relname, attnum, + NameStr(pg_attribute_tuple->attname)); ++ /* SELinux checks */ ++ sepgsql_attribute_grant(relOid, attnum); + + /* + * Generate new ACL. +*************** ExecGrant_Relation(InternalGrant *istmt) +*** 1029,1034 **** +--- 1039,1046 ---- + ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, + NameStr(pg_class_tuple->relname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_relation_grant(relOid); + + /* + * Generate new ACL. +*************** ExecGrant_Database(InternalGrant *istmt) +*** 1217,1222 **** +--- 1229,1236 ---- + datId, grantorId, ACL_KIND_DATABASE, + NameStr(pg_database_tuple->datname), + 0, NULL); ++ /* SELinux permission checks */ ++ sepgsql_database_grant(datId); + + /* + * Generate new ACL. +*************** ExecGrant_Fdw(InternalGrant *istmt) +*** 1335,1340 **** +--- 1349,1356 ---- + fdwid, grantorId, ACL_KIND_FDW, + NameStr(pg_fdw_tuple->fdwname), + 0, NULL); ++ /* SELinux permission checks */ ++ sepgsql_fdw_grant(fdwid); + + /* + * Generate new ACL. +*************** ExecGrant_ForeignServer(InternalGrant *i +*** 1454,1459 **** +--- 1470,1477 ---- + srvid, grantorId, ACL_KIND_FOREIGN_SERVER, + NameStr(pg_server_tuple->srvname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_foreign_server_grant(srvid); + + /* + * Generate new ACL. *************** ExecGrant_Function(InternalGrant *istmt) *** 1572,1577 **** ---- 1580,1587 ---- +--- 1590,1597 ---- funcId, grantorId, ACL_KIND_PROC, NameStr(pg_proc_tuple->proname), 0, NULL); + /* SELinux: db_procedure:{setattr} */ -+ sepgsqlCheckProcedureSetattr(funcId); ++ sepgsql_proc_grant(funcId); + + /* + * Generate new ACL. +*************** ExecGrant_Language(InternalGrant *istmt) +*** 1696,1701 **** +--- 1716,1723 ---- + langId, grantorId, ACL_KIND_LANGUAGE, + NameStr(pg_language_tuple->lanname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_language_grant(langId); /* * Generate new ACL. *************** ExecGrant_Namespace(InternalGrant *istmt *** 1815,1820 **** ---- 1825,1833 ---- +--- 1837,1845 ---- NameStr(pg_namespace_tuple->nspname), 0, NULL); + /* SELinux: db_schema:{setattr} */ -+ sepgsqlCheckSchemaSetattr(nspid); ++ sepgsql_schema_grant(nspid); + /* * Generate new ACL. @@ -817,7 +921,7 @@ diff -Nrpc base/src/backend/catalog/catalog.c sepgsql/src/backend/catalog/catalo return true; diff -Nrpc base/src/backend/catalog/dependency.c sepgsql/src/backend/catalog/dependency.c *** base/src/backend/catalog/dependency.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/catalog/dependency.c Mon Sep 7 00:55:45 2009 +--- sepgsql/src/backend/catalog/dependency.c Thu Sep 17 23:44:07 2009 *************** *** 63,68 **** --- 63,69 ---- @@ -968,7 +1072,7 @@ diff -Nrpc base/src/backend/catalog/dependency.c sepgsql/src/backend/catalog/dep + /* SELinux checks db_xxx:{drop}, if necessary */ + if (permission) -+ sepgsqlCheckSysobjDrop(object); ++ sepgsql_sysobj_drop(object); + /* * First remove any pg_depend records that link from this object to @@ -1332,7 +1436,7 @@ diff -Nrpc base/src/backend/catalog/heap.c sepgsql/src/backend/catalog/heap.c diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c *** base/src/backend/catalog/index.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/index.c Wed Jul 15 19:39:56 2009 +--- sepgsql/src/backend/catalog/index.c Fri Sep 18 17:02:48 2009 *************** *** 48,53 **** --- 48,54 ---- @@ -1343,49 +1447,6 @@ diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/procarray.h" -*************** static TupleDesc ConstructTupleDescripto -*** 81,87 **** - Oid *classObjectId); - static void InitializeAttributeOids(Relation indexRelation, - int numatts, Oid indexoid); -! static void AppendAttributeTuples(Relation indexRelation, int numatts); - static void UpdateIndexRelation(Oid indexoid, Oid heapoid, - IndexInfo *indexInfo, - Oid *classOids, ---- 82,88 ---- - Oid *classObjectId); - static void InitializeAttributeOids(Relation indexRelation, - int numatts, Oid indexoid); -! static void AppendAttributeTuples(Relation indexRelation, int numatts, Oid *secLabels); - static void UpdateIndexRelation(Oid indexoid, Oid heapoid, - IndexInfo *indexInfo, - Oid *classOids, -*************** InitializeAttributeOids(Relation indexRe -*** 323,333 **** - * ---------------------------------------------------------------- - */ - static void -! AppendAttributeTuples(Relation indexRelation, int numatts) - { - Relation pg_attribute; - CatalogIndexState indstate; - TupleDesc indexTupDesc; - int i; - - /* ---- 324,335 ---- - * ---------------------------------------------------------------- - */ - static void -! AppendAttributeTuples(Relation indexRelation, int numatts, Oid *secLabels) - { - Relation pg_attribute; - CatalogIndexState indstate; - TupleDesc indexTupDesc; -+ Oid attsid = InvalidOid; - int i; - - /* *************** AppendAttributeTuples(Relation indexRela *** 351,357 **** Assert(indexTupDesc->attrs[i]->attnum == i + 1); @@ -1395,47 +1456,16 @@ diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c } CatalogCloseIndexes(indstate); ---- 353,363 ---- +--- 352,359 ---- Assert(indexTupDesc->attrs[i]->attnum == i + 1); Assert(indexTupDesc->attrs[i]->attcacheoff == -1); -! if (secLabels) -! attsid = secLabels[i - FirstLowInvalidHeapAttributeNumber]; -! ! InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], -! indstate, attsid); +! indstate, InvalidOid); } CatalogCloseIndexes(indstate); *************** index_create(Oid heapRelationId, -*** 519,524 **** ---- 525,532 ---- - TupleDesc indexTupDesc; - bool shared_relation; - Oid namespaceId; -+ Oid *secLabels; -+ Oid relsid = InvalidOid; - int i; - - pg_class = heap_open(RelationRelationId, RowExclusiveLock); -*************** index_create(Oid heapRelationId, -*** 604,609 **** ---- 612,625 ---- - classObjectId); - - /* -+ * compute security labels to be assigned on index relation -+ */ -+ secLabels = sepgsqlCreateTableColumns(NULL, indexRelationName, namespaceId, -+ indexTupDesc, RELKIND_INDEX); -+ if (secLabels) -+ relsid = secLabels[0]; -+ -+ /* - * Allocate an OID for the index, unless we were told what to use. - * - * The OID will be the relfilenode as well, so make sure it doesn't -*************** index_create(Oid heapRelationId, *** 652,658 **** */ InsertPgClassTuple(pg_class, indexRelation, @@ -1444,34 +1474,17 @@ diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c /* done with pg_class */ heap_close(pg_class, RowExclusiveLock); ---- 668,674 ---- +--- 654,660 ---- */ InsertPgClassTuple(pg_class, indexRelation, RelationGetRelid(indexRelation), -! reloptions, relsid); +! reloptions, InvalidOid); /* done with pg_class */ heap_close(pg_class, RowExclusiveLock); -*************** index_create(Oid heapRelationId, -*** 668,674 **** - /* - * append ATTRIBUTE tuples for the index - */ -! AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs); - - /* ---------------- - * update pg_index ---- 684,690 ---- - /* - * append ATTRIBUTE tuples for the index - */ -! AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs, secLabels); - - /* ---------------- - * update pg_index diff -Nrpc base/src/backend/catalog/namespace.c sepgsql/src/backend/catalog/namespace.c *** base/src/backend/catalog/namespace.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/namespace.c Mon Sep 7 00:55:45 2009 +--- sepgsql/src/backend/catalog/namespace.c Thu Sep 17 17:04:16 2009 *************** *** 39,44 **** --- 39,45 ---- @@ -1489,7 +1502,7 @@ diff -Nrpc base/src/backend/catalog/namespace.c sepgsql/src/backend/catalog/name { if (OidIsValid(myTempNamespace)) + { -+ sepgsqlCheckSchemaSearch(myTempNamespace, true); ++ sepgsql_schema_search(myTempNamespace, true); return myTempNamespace; + } @@ -1501,7 +1514,7 @@ diff -Nrpc base/src/backend/catalog/namespace.c sepgsql/src/backend/catalog/name if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, nspname); -+ sepgsqlCheckSchemaSearch(namespaceId, true); ++ sepgsql_schema_search(namespaceId, true); return namespaceId; } @@ -1519,7 +1532,7 @@ diff -Nrpc base/src/backend/catalog/namespace.c sepgsql/src/backend/catalog/name !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, ! ACL_USAGE) == ACLCHECK_OK && -! sepgsqlCheckSchemaSearch(namespaceId, false)) +! sepgsql_schema_search(namespaceId, false)) oidlist = lappend_oid(oidlist, namespaceId); } } @@ -1537,7 +1550,7 @@ diff -Nrpc base/src/backend/catalog/namespace.c sepgsql/src/backend/catalog/name if (OidIsValid(myTempNamespace)) { ! if (!list_member_oid(oidlist, myTempNamespace) && -! sepgsqlCheckSchemaSearch(myTempNamespace, false)) +! sepgsql_schema_search(myTempNamespace, false)) oidlist = lappend_oid(oidlist, myTempNamespace); } else @@ -1555,7 +1568,7 @@ diff -Nrpc base/src/backend/catalog/namespace.c sepgsql/src/backend/catalog/name !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, ! ACL_USAGE) == ACLCHECK_OK && -! sepgsqlCheckSchemaSearch(namespaceId, false)) +! sepgsql_schema_search(namespaceId, false)) oidlist = lappend_oid(oidlist, namespaceId); } } @@ -1576,7 +1589,7 @@ diff -Nrpc base/src/backend/catalog/namespace.c sepgsql/src/backend/catalog/name get_database_name(MyDatabaseId)))); + /* SELinux checks permission to create temp schema */ -+ nspsecid = sepgsqlCheckSchemaCreate(namespaceName, NULL, true); ++ nspsecid = sepgsql_schema_create(namespaceName, true, NULL); + snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); @@ -1656,6 +1669,37 @@ diff -Nrpc base/src/backend/catalog/pg_aggregate.c sepgsql/src/backend/catalog/p /* * Okay to create the pg_aggregate entry. +diff -Nrpc base/src/backend/catalog/pg_conversion.c sepgsql/src/backend/catalog/pg_conversion.c +*** base/src/backend/catalog/pg_conversion.c Sat Jan 3 13:01:35 2009 +--- sepgsql/src/backend/catalog/pg_conversion.c Thu Sep 17 22:10:19 2009 +*************** Oid +*** 40,46 **** + ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +! Oid conproc, bool def) + { + int i; + Relation rel; +--- 40,46 ---- + ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +! Oid conproc, Oid consecid, bool def) + { + int i; + Relation rel; +*************** ConversionCreate(const char *conname, Oi +*** 104,109 **** +--- 104,111 ---- + values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def); + + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, consecid); + + /* insert a new tuple */ + oid = simple_heap_insert(rel, tup); diff -Nrpc base/src/backend/catalog/pg_largeobject.c sepgsql/src/backend/catalog/pg_largeobject.c *** base/src/backend/catalog/pg_largeobject.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/catalog/pg_largeobject.c Wed Jul 15 19:48:58 2009 @@ -1724,9 +1768,101 @@ diff -Nrpc base/src/backend/catalog/pg_namespace.c sepgsql/src/backend/catalog/p nspoid = simple_heap_insert(nspdesc, tup); Assert(OidIsValid(nspoid)); +diff -Nrpc base/src/backend/catalog/pg_operator.c sepgsql/src/backend/catalog/pg_operator.c +*** base/src/backend/catalog/pg_operator.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/catalog/pg_operator.c Thu Sep 17 22:10:19 2009 +*************** +*** 28,33 **** +--- 28,34 ---- + #include "catalog/pg_type.h" + #include "miscadmin.h" + #include "parser/parse_oper.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +*************** OperatorShellMake(const char *operatorNa +*** 204,209 **** +--- 205,211 ---- + { + Relation pg_operator_desc; + Oid operatorObjectId; ++ Oid secid; + int i; + HeapTuple tup; + Datum values[Natts_pg_operator]; +*************** OperatorShellMake(const char *operatorNa +*** 220,225 **** +--- 222,231 ---- + errmsg("\"%s\" is not a valid operator name", + operatorName))); + ++ /* SELinux permission check */ ++ secid = sepgsql_operator_create(operatorName, InvalidOid, ++ operatorNamespace, ++ InvalidOid, InvalidOid, InvalidOid); + /* + * initialize our *nulls and *values arrays + */ +*************** OperatorShellMake(const char *operatorNa +*** 260,265 **** +--- 266,273 ---- + * create a new operator tuple + */ + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup) && OidIsValid(secid)) ++ HeapTupleSetSecid(tup, secid); + + /* + * insert our "shell" operator tuple +*************** OperatorCreate(const char *operatorName, +*** 347,352 **** +--- 355,361 ---- + bool selfCommutator = false; + NameData oname; + TupleDesc tupDesc; ++ Oid secid; + int i; + + /* +*************** OperatorCreate(const char *operatorName, +*** 476,481 **** +--- 485,494 ---- + else + negatorId = InvalidOid; + ++ /* SELinux permission checks */ ++ secid = sepgsql_operator_create(operatorName, operatorObjectId, ++ operatorNamespace, ++ procedureId, restrictionId, joinId); + /* + * set up values in the operator tuple + */ +*************** OperatorCreate(const char *operatorName, +*** 523,528 **** +--- 536,543 ---- + values, + nulls, + replaces); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + simple_heap_update(pg_operator_desc, &tup->t_self, tup); + } +*************** OperatorCreate(const char *operatorName, +*** 530,535 **** +--- 545,552 ---- + { + tupDesc = pg_operator_desc->rd_att; + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + operatorObjectId = simple_heap_insert(pg_operator_desc, tup); + } diff -Nrpc base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_proc.c *** base/src/backend/catalog/pg_proc.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/pg_proc.c Tue Sep 8 23:55:48 2009 +--- sepgsql/src/backend/catalog/pg_proc.c Fri Sep 18 17:39:46 2009 *************** *** 29,34 **** --- 29,35 ---- @@ -1767,37 +1903,32 @@ diff -Nrpc base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_pro bool is_update; *************** ProcedureCreate(const char *procedureNam *** 343,348 **** ---- 346,361 ---- +--- 346,356 ---- ObjectIdGetDatum(procNamespace), 0); + /* Check permission to create/replace a function */ -+ prosecid = sepgsqlCheckProcedureCreate(procedureName, -+ HeapTupleIsValid(oldtup) -+ ? HeapTupleGetOid(oldtup) -+ : InvalidOid, -+ procNamespace, -+ languageObjectId, -+ (DefElem *)proseclabel); -+ sepgsqlCheckSchemaAddName(procNamespace); ++ prosecid = sepgsql_proc_create(procedureName, oldtup, ++ procNamespace, languageObjectId, ++ (DefElem *)proseclabel); + if (HeapTupleIsValid(oldtup)) { /* There is one; okay to replace it? */ *************** ProcedureCreate(const char *procedureNam *** 477,482 **** ---- 490,497 ---- +--- 485,492 ---- /* Okay, do it... */ tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); -+ if (HeapTupleHasSecid(tup) && OidIsValid(prosecid)) ++ if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, prosecid); simple_heap_update(rel, &tup->t_self, tup); ReleaseSysCache(oldtup); *************** ProcedureCreate(const char *procedureNam *** 486,491 **** ---- 501,508 ---- +--- 496,503 ---- { /* Creating a new procedure */ tup = heap_form_tuple(tupDesc, values, nulls); @@ -2451,7 +2582,7 @@ diff -Nrpc base/src/backend/catalog/pg_security.c sepgsql/src/backend/catalog/pg + } diff -Nrpc base/src/backend/catalog/pg_shdepend.c sepgsql/src/backend/catalog/pg_shdepend.c *** base/src/backend/catalog/pg_shdepend.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/pg_shdepend.c Tue Sep 8 23:55:48 2009 +--- sepgsql/src/backend/catalog/pg_shdepend.c Fri Sep 18 14:51:00 2009 *************** *** 36,41 **** --- 36,42 ---- @@ -2464,23 +2595,12 @@ diff -Nrpc base/src/backend/catalog/pg_shdepend.c sepgsql/src/backend/catalog/pg #include "utils/acl.h" *************** shdepReassignOwned(List *roleids, Oid ne *** 1336,1341 **** ---- 1337,1355 ---- +--- 1337,1344 ---- break; case TypeRelationId: -+ /* -+ * NOTE: SELinux does not check anything inside -+ * AlterTypeOwnerInternal() -+ */ -+ tuple = SearchSysCache(TYPEOID, -+ ObjectIdGetDatum(sdepForm->objid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for type: %u", sdepForm->objid); -+ sepgsqlCheckSysobjSetattr(TypeRelationId, -+ HeapTupleGetSecid(tuple), -+ NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname)); -+ ReleaseSysCache(tuple); ++ /* SELinux checks */ ++ sepgsql_type_alter(sdepForm->objid, NULL, InvalidOid); AlterTypeOwnerInternal(sdepForm->objid, newrole, true); break; @@ -2493,17 +2613,18 @@ diff -Nrpc base/src/backend/catalog/pg_shdepend.c sepgsql/src/backend/catalog/pg /* * Pass recursing = true so that we don't fail on indexes, * owned sequences, etc when we happen to visit them ---- 1362,1368 ---- +--- 1351,1358 ---- break; case RelationRelationId: -! sepgsqlCheckTableSetattr(sdepForm->objid); +! /* SELinux checks */ +! sepgsql_relation_alter(sdepForm->objid, NULL, InvalidOid); /* * Pass recursing = true so that we don't fail on indexes, * owned sequences, etc when we happen to visit them diff -Nrpc base/src/backend/catalog/pg_type.c sepgsql/src/backend/catalog/pg_type.c *** base/src/backend/catalog/pg_type.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/catalog/pg_type.c Tue Sep 8 23:55:48 2009 +--- sepgsql/src/backend/catalog/pg_type.c Fri Sep 18 17:39:46 2009 *************** *** 25,30 **** --- 25,31 ---- @@ -2516,7 +2637,7 @@ diff -Nrpc base/src/backend/catalog/pg_type.c sepgsql/src/backend/catalog/pg_typ #include "utils/fmgroids.h" *************** TypeShellMake(const char *typeName, Oid *** 56,65 **** ---- 57,72 ---- +--- 57,73 ---- Datum values[Natts_pg_type]; bool nulls[Natts_pg_type]; Oid typoid; @@ -2526,16 +2647,17 @@ diff -Nrpc base/src/backend/catalog/pg_type.c sepgsql/src/backend/catalog/pg_typ Assert(PointerIsValid(typeName)); + /* SELinux check permission to create a shell type */ -+ typsid = sepgsqlCheckSysobjCreate(TypeRelationId, typeName); -+ sepgsqlCheckProcedureInstall(F_SHELL_IN); -+ sepgsqlCheckProcedureInstall(F_SHELL_OUT); ++ 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 **** ---- 208,214 ---- +--- 209,215 ---- { Relation pg_type_desc; Oid typeObjectId; @@ -2545,30 +2667,25 @@ diff -Nrpc base/src/backend/catalog/pg_type.c sepgsql/src/backend/catalog/pg_typ bool nulls[Natts_pg_type]; *************** TypeCreate(Oid newTypeOid, *** 367,372 **** ---- 375,394 ---- +--- 376,390 ---- CStringGetDatum(typeName), ObjectIdGetDatum(typeNamespace), 0, 0); + + /* SELinux checks to create/replace type */ + if (!isImplicitArray && typeType != TYPTYPE_COMPOSITE) -+ { -+ if (!HeapTupleIsValid(tup)) -+ typeSecid = sepgsqlCheckSysobjCreate(TypeRelationId, typeName); -+ else -+ { -+ typeSecid = HeapTupleGetSecid(tup); -+ sepgsqlCheckSysobjSetattr(TypeRelationId, typeSecid, typeName); -+ } -+ sepgsqlCheckSchemaAddName(typeNamespace); -+ } ++ typeSecid = sepgsql_type_create(typeName, tup, typeNamespace, ++ inputProcedure, outputProcedure, ++ receiveProcedure, sendProcedure, ++ typmodinProcedure, typmodoutProcedure, ++ analyzeProcedure); + if (HeapTupleIsValid(tup)) { /* *************** TypeCreate(Oid newTypeOid, *** 412,417 **** ---- 434,441 ---- +--- 430,437 ---- /* Force the OID if requested by caller, else heap_insert does it */ if (OidIsValid(newTypeOid)) HeapTupleSetOid(tup, newTypeOid); @@ -2579,7 +2696,7 @@ diff -Nrpc base/src/backend/catalog/pg_type.c sepgsql/src/backend/catalog/pg_typ } diff -Nrpc base/src/backend/catalog/toasting.c sepgsql/src/backend/catalog/toasting.c *** base/src/backend/catalog/toasting.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/toasting.c Wed Jul 15 19:35:52 2009 +--- sepgsql/src/backend/catalog/toasting.c Thu Oct 8 09:29:32 2009 *************** *** 28,33 **** --- 28,34 ---- @@ -2602,13 +2719,14 @@ diff -Nrpc base/src/backend/catalog/toasting.c sepgsql/src/backend/catalog/toast ObjectAddress baseobject, *************** create_toast_table(Relation rel, Oid toa *** 199,204 **** ---- 201,210 ---- +--- 201,211 ---- else namespaceid = PG_TOAST_NAMESPACE; -+ secLabels = sepgsqlCreateTableColumns(NULL, -+ toast_relname, namespaceid, -+ tupdesc, RELKIND_TOASTVALUE); ++ secLabels = sepgsql_relation_create(toast_relname, ++ RELKIND_TOASTVALUE, ++ tupdesc, namespaceid, ++ NULL, NIL, false, false); + toast_relid = heap_create_with_catalog(toast_relname, namespaceid, @@ -2622,7 +2740,7 @@ diff -Nrpc base/src/backend/catalog/toasting.c sepgsql/src/backend/catalog/toast /* make the toast relation visible, else index creation will fail */ CommandCounterIncrement(); ---- 218,225 ---- +--- 219,226 ---- 0, ONCOMMIT_NOOP, reloptions, @@ -2633,7 +2751,7 @@ diff -Nrpc base/src/backend/catalog/toasting.c sepgsql/src/backend/catalog/toast CommandCounterIncrement(); diff -Nrpc base/src/backend/commands/aggregatecmds.c sepgsql/src/backend/commands/aggregatecmds.c *** base/src/backend/commands/aggregatecmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/aggregatecmds.c Mon Sep 7 00:55:45 2009 +--- sepgsql/src/backend/commands/aggregatecmds.c Thu Sep 17 22:10:19 2009 *************** *** 32,37 **** --- 32,38 ---- @@ -2646,46 +2764,22 @@ diff -Nrpc base/src/backend/commands/aggregatecmds.c sepgsql/src/backend/command #include "utils/lsyscache.h" *************** RenameAggregate(List *name, List *args, *** 311,316 **** ---- 312,322 ---- +--- 312,320 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux permission checks */ -+ sepgsqlCheckProcedureSetattr(procOid); -+ sepgsqlCheckSchemaRemoveName(namespaceOid); -+ sepgsqlCheckSchemaAddName(namespaceOid); ++ sepgsql_proc_alter(procOid, newname, InvalidOid); + /* rename */ namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname); simple_heap_update(rel, &tup->t_self, tup); diff -Nrpc base/src/backend/commands/alter.c sepgsql/src/backend/commands/alter.c *** base/src/backend/commands/alter.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/commands/alter.c Thu Jul 16 17:22:29 2009 -*************** -*** 28,33 **** ---- 28,34 ---- - #include "commands/user.h" - #include "miscadmin.h" - #include "parser/parse_clause.h" -+ #include "security/sepgsql.h" - #include "tcop/utility.h" - #include "utils/acl.h" - #include "utils/lsyscache.h" -*************** ExecRenameStmt(RenameStmt *stmt) -*** 117,122 **** ---- 118,126 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceId)); - -+ /* SELinux checks db_table/db_sequence:{setattr} */ -+ sepgsqlCheckTableSetattr(relid); -+ - RenameRelation(relid, stmt->newname, stmt->renameType); - break; - } +--- sepgsql/src/backend/commands/alter.c Fri Sep 18 14:51:00 2009 *************** ExecAlterOwnerStmt(AlterOwnerStmt *stmt) *** 284,286 **** ---- 288,319 ---- +--- 284,315 ---- (int) stmt->objectType); } } @@ -2720,7 +2814,7 @@ diff -Nrpc base/src/backend/commands/alter.c sepgsql/src/backend/commands/alter. + } diff -Nrpc base/src/backend/commands/cluster.c sepgsql/src/backend/commands/cluster.c *** base/src/backend/commands/cluster.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/cluster.c Tue Sep 8 23:55:48 2009 +--- sepgsql/src/backend/commands/cluster.c Fri Sep 18 14:51:00 2009 *************** *** 36,41 **** --- 36,42 ---- @@ -2765,7 +2859,7 @@ diff -Nrpc base/src/backend/commands/cluster.c sepgsql/src/backend/commands/clus ONCOMMIT_NOOP, reloptions, ! allowSystemTableMods, -! sepgsqlCopyTableColumns(OldHeap)); +! sepgsql_relation_copy(OldHeap)); ReleaseSysCache(tuple); @@ -2784,7 +2878,7 @@ diff -Nrpc base/src/backend/commands/cluster.c sepgsql/src/backend/commands/clus diff -Nrpc base/src/backend/commands/conversioncmds.c sepgsql/src/backend/commands/conversioncmds.c *** base/src/backend/commands/conversioncmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/conversioncmds.c Wed Jul 15 19:48:58 2009 +--- sepgsql/src/backend/commands/conversioncmds.c Thu Sep 17 22:10:19 2009 *************** *** 24,29 **** --- 24,30 ---- @@ -2796,20 +2890,70 @@ diff -Nrpc base/src/backend/commands/conversioncmds.c sepgsql/src/backend/comman #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 **** ---- 97,105 ---- +--- 98,106 ---- aclcheck_error(aclresult, ACL_KIND_PROC, NameListToString(func_name)); -+ /* SELinux checks db_procedure:{install} */ -+ sepgsqlCheckProcedureInstall(funcoid); ++ /* SELinux checks */ ++ secid = sepgsql_conversion_create(conversion_name, namespaceId, funcoid); + /* * Check that the conversion function is suitable for the requested source * and target encodings. We do that by calling the function with an empty +*************** CreateConversionCommand(CreateConversion +*** 114,120 **** + * name) + */ + ConversionCreate(conversion_name, namespaceId, GetUserId(), +! from_encoding, to_encoding, funcoid, stmt->def); + } + + /* +--- 119,125 ---- + * name) + */ + ConversionCreate(conversion_name, namespaceId, GetUserId(), +! from_encoding, to_encoding, funcoid, secid, stmt->def); + } + + /* +*************** RenameConversion(List *name, const char +*** 240,245 **** +--- 245,253 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_conversion_alter(conversionOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); + simple_heap_update(rel, &tup->t_self, tup); +*************** AlterConversionOwner_internal(Relation r +*** 336,341 **** +--- 344,351 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(convForm->connamespace)); + } ++ /* SELinux checks */ ++ sepgsql_conversion_alter(HeapTupleGetOid(tup), NULL); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c *** base/src/backend/commands/copy.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/copy.c Thu Sep 10 15:18:03 2009 +--- sepgsql/src/backend/commands/copy.c Mon Sep 28 09:29:32 2009 *************** *** 21,28 **** --- 21,31 ---- @@ -2868,8 +3012,42 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c 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 **** ---- 1099,1107 ---- +--- 1106,1114 ---- num_phys_attrs = tupDesc->natts; @@ -2892,7 +3070,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c } } ---- 1116,1146 ---- +--- 1123,1153 ---- int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) @@ -2936,7 +3114,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c cstate->force_notnull_flags[attnum - 1] = true; } } ---- 1158,1180 ---- +--- 1165,1187 ---- int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) @@ -2960,18 +3138,6 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c cstate->force_notnull_flags[attnum - 1] = true; } } -*************** DoCopyTo(CopyState cstate) -*** 1258,1263 **** ---- 1303,1311 ---- - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a directory", cstate->filename))); -+ -+ /* SELinux checks file:{write} permission */ -+ sepgsqlCheckFileWrite(fileno(cstate->copy_file), cstate->filename); - } - - PG_TRY(); *************** CopyTo(CopyState cstate) *** 1321,1336 **** int attnum = lfirst_int(cur); @@ -2990,7 +3156,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c } /* ---- 1369,1399 ---- +--- 1373,1403 ---- int attnum = lfirst_int(cur); Oid out_func_oid; bool isvarlena; @@ -3031,7 +3197,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c CopyAttributeOutCSV(cstate, colname, false, list_length(cstate->attnumlist) == 1); ---- 1448,1461 ---- +--- 1452,1465 ---- CopySendChar(cstate, cstate->delim[0]); hdr_delim = true; @@ -3059,7 +3225,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c } heap_endscan(scandesc); ---- 1481,1495 ---- +--- 1485,1499 ---- { CHECK_FOR_INTERRUPTS(); @@ -3084,7 +3250,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c { bool need_delim = false; FmgrInfo *out_functions = cstate->out_functions; ---- 1515,1522 ---- +--- 1519,1526 ---- * Emit one row during CopyTo(). */ static void @@ -3102,7 +3268,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c } } else ---- 1536,1542 ---- +--- 1540,1546 ---- { /* Hack --- assume Oid is same size as int32 */ CopySendInt32(cstate, sizeof(int32)); @@ -3119,7 +3285,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c CopySendString(cstate, string); need_delim = true; } ---- 1546,1552 ---- +--- 1550,1556 ---- if (cstate->oids) { string = DatumGetCString(DirectFunctionCall1(oidout, @@ -3137,7 +3303,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c if (!cstate->binary) { ---- 1555,1565 ---- +--- 1559,1569 ---- foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); @@ -3151,7 +3317,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c { *************** CopyOneRowTo(CopyState cstate, Oid tuple *** 1490,1495 **** ---- 1568,1591 ---- +--- 1572,1595 ---- need_delim = true; } @@ -3189,7 +3355,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c list_length(cstate->attnumlist) == 1); else CopyAttributeOutText(cstate, string); ---- 1597,1605 ---- +--- 1601,1609 ---- { if (!cstate->binary) { @@ -3209,7 +3375,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); CopySendData(cstate, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); ---- 1608,1614 ---- +--- 1612,1618 ---- { bytea *outputbytes; @@ -3219,7 +3385,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c VARSIZE(outputbytes) - VARHDRSZ); *************** CopyFrom(CopyState cstate) *** 1649,1656 **** ---- 1742,1751 ---- +--- 1746,1755 ---- num_defaults; FmgrInfo *in_functions; FmgrInfo oid_in_function; @@ -3231,20 +3397,8 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c int i; Oid in_func_oid; *************** CopyFrom(CopyState cstate) -*** 1753,1758 **** ---- 1848,1856 ---- - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a directory", cstate->filename))); -+ -+ /* SELinux checks file:{read} permission */ -+ sepgsqlCheckFileRead(fileno(cstate->copy_file), cstate->filename); - } - - tupDesc = RelationGetDescr(cstate->rel); -*************** CopyFrom(CopyState cstate) *** 1888,1893 **** ---- 1986,2003 ---- +--- 1987,2004 ---- fmgr_info(in_func_oid, &oid_in_function); } @@ -3265,7 +3419,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c *************** CopyFrom(CopyState cstate) *** 1922,1927 **** ---- 2032,2038 ---- +--- 2033,2039 ---- { bool skip_tuple; Oid loaded_oid = InvalidOid; @@ -3289,7 +3443,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c string = field_strings[fieldno++]; if (cstate->csv_mode && string == NULL && ---- 2104,2124 ---- +--- 2105,2125 ---- /* Loop to read the user attributes on the line. */ foreach(cur, cstate->attnumlist) { @@ -3327,7 +3481,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c cstate->cur_attname = NULL; cstate->cur_attval = NULL; } ---- 2128,2167 ---- +--- 2129,2168 ---- string = cstate->null_print; } @@ -3387,7 +3541,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c cstate->cur_attname = NULL; } } ---- 2207,2247 ---- +--- 2208,2248 ---- i = 0; foreach(cur, cstate->attnumlist) { @@ -3431,7 +3585,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c } *************** CopyFrom(CopyState cstate) *** 2094,2099 **** ---- 2262,2269 ---- +--- 2263,2270 ---- if (cstate->oids && file_has_oids) HeapTupleSetOid(tuple, loaded_oid); @@ -3442,7 +3596,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c MemoryContextSwitchTo(oldcontext); *************** CopyFrom(CopyState cstate) *** 2118,2123 **** ---- 2288,2296 ---- +--- 2289,2297 ---- } if (!skip_tuple) @@ -3454,7 +3608,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c ExecStoreTuple(tuple, slot, InvalidBuffer, false); *************** CopyGetAttnums(TupleDesc tupDesc, Relati *** 3398,3403 **** ---- 3571,3583 ---- +--- 3572,3584 ---- } if (attnum == InvalidAttrNumber) { @@ -3477,7 +3631,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c } /* ---- 3625,3632 ---- +--- 3626,3633 ---- slot_getallattrs(slot); /* And send the data */ @@ -3488,7 +3642,7 @@ diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c /* diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/dbcommands.c *** base/src/backend/commands/dbcommands.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/dbcommands.c Tue Sep 8 23:55:48 2009 +--- sepgsql/src/backend/commands/dbcommands.c Thu Sep 17 17:04:16 2009 *************** *** 33,38 **** --- 33,39 ---- @@ -3553,7 +3707,7 @@ diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/d check_is_member_of_role(GetUserId(), datdba); + /* SELinux checks db_database:{create} */ -+ datsecid = sepgsqlCheckDatabaseCreate(dbname, dseclabel); ++ datsecid = sepgsql_database_create(dbname, dseclabel); + /* * Lookup database (template) to be cloned, and obtain share lock on it. @@ -3588,7 +3742,7 @@ diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/d dbname); + /* SELinux checks db_database:{drop} permission */ -+ sepgsqlCheckDatabaseDrop(db_id); ++ sepgsql_database_drop(db_id); + /* * Disallow dropping a DB that is marked istemplate. This is just to @@ -3614,7 +3768,7 @@ diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/d errmsg("permission denied to rename database"))); + /* SELinux: check db_database:{setattr} */ -+ sepgsqlCheckDatabaseSetattr(db_id); ++ sepgsql_database_alter(db_id); + /* * Make sure the new name doesn't exist. See notes for same error in @@ -3626,7 +3780,7 @@ diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/d dbname); + /* SELinux checks db_database:{setattr} */ -+ sepgsqlCheckDatabaseSetattr(db_id); ++ sepgsql_database_alter(db_id); + /* * Obviously can't move the tables of my own database @@ -3638,7 +3792,7 @@ diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/d stmt->dbname); + /* SELinux checks db_database:{setattr} */ -+ sepgsqlCheckDatabaseSetattr(HeapTupleGetOid(tuple)); ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); + /* * Build an updated tuple, perusing the information just obtained @@ -3650,7 +3804,7 @@ diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/d stmt->dbname); + /* SELinux checks db_database:{setattr} */ -+ sepgsqlCheckDatabaseSetattr(HeapTupleGetOid(tuple)); ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); + memset(repl_repl, false, sizeof(repl_repl)); repl_repl[Anum_pg_database_datconfig - 1] = true; @@ -3662,7 +3816,7 @@ diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/d errmsg("permission denied to change owner of database"))); + /* SELinux checks db_database:{setattr} */ -+ sepgsqlCheckDatabaseSetattr(HeapTupleGetOid(tuple)); ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); + memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); @@ -3704,6 +3858,10 @@ diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/d + 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 */ @@ -3711,11 +3869,7 @@ diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/d + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); + + /* SELinux checks db_database:{setattr relabelfrom relabelto} */ -+ secid = sepgsqlCheckDatabaseRelabel(HeapTupleGetOid(newtup), seclabel); -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", dbname))); ++ secid = sepgsql_database_relabel(HeapTupleGetOid(newtup), seclabel); + HeapTupleSetSecid(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); @@ -3730,7 +3884,7 @@ diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/d * Helper functions diff -Nrpc base/src/backend/commands/foreigncmds.c sepgsql/src/backend/commands/foreigncmds.c *** base/src/backend/commands/foreigncmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/foreigncmds.c Wed Jul 15 19:48:58 2009 +--- sepgsql/src/backend/commands/foreigncmds.c Thu Sep 17 22:10:19 2009 *************** *** 27,32 **** --- 27,33 ---- @@ -3741,33 +3895,121 @@ diff -Nrpc base/src/backend/commands/foreigncmds.c sepgsql/src/backend/commands/ #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" +*************** AlterForeignDataWrapperOwner(const char +*** 233,238 **** +--- 234,242 ---- + + if (form->fdwowner != newOwnerId) + { ++ /* SELinux permission check */ ++ sepgsql_fdw_alter(fdwId, InvalidOid); ++ + form->fdwowner = newOwnerId; + + simple_heap_update(rel, &tup->t_self, tup); +*************** AlterForeignServerOwner(const char *name +*** 297,302 **** +--- 301,308 ---- + aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + } + } ++ /* SELinux permission checks */ ++ sepgsql_foreign_server_alter(srvId); + + form->srvowner = newOwnerId; + +*************** CreateForeignDataWrapper(CreateFdwStmt * +*** 342,347 **** +--- 348,354 ---- + Oid fdwvalidator; + Datum fdwoptions; + Oid ownerId; ++ Oid secid; + + /* Must be super user */ + if (!superuser()) *************** CreateForeignDataWrapper(CreateFdwStmt * *** 380,385 **** ---- 381,389 ---- +--- 387,395 ---- else fdwvalidator = InvalidOid; -+ /* SELinux checks db_procedure:{install} */ -+ sepgsqlCheckProcedureInstall(fdwvalidator); ++ /* SELinux permission checks */ ++ secid = sepgsql_fdw_create(stmt->fdwname, fdwvalidator); + values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = fdwvalidator; nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true; +*************** CreateForeignDataWrapper(CreateFdwStmt * +*** 393,398 **** +--- 403,410 ---- + nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; + + tuple = heap_form_tuple(rel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, secid); + + fdwId = simple_heap_insert(rel, tuple); + CatalogUpdateIndexes(rel, tuple); *************** AlterForeignDataWrapper(AlterFdwStmt *st -*** 473,478 **** ---- 477,485 ---- - ereport(WARNING, - (errmsg("changing the foreign-data wrapper validator can cause " - "the options for dependent objects to become invalid"))); -+ -+ /* SELinux checks db_procedure:{install} */ -+ sepgsqlCheckProcedureInstall(fdwvalidator); +*** 487,492 **** +--- 499,507 ---- + fdwvalidator = DatumGetObjectId(datum); } - else - { + ++ /* SELinux permission checks */ ++ sepgsql_fdw_alter(fdwId, fdwvalidator); ++ + /* + * Options specified, validate and update. + */ +*************** CreateForeignServer(CreateForeignServerS +*** 609,614 **** +--- 624,630 ---- + HeapTuple tuple; + Oid srvId; + Oid ownerId; ++ Oid secid; + AclResult aclresult; + ObjectAddress myself; + ObjectAddress referenced; +*************** CreateForeignServer(CreateForeignServerS +*** 636,641 **** +--- 652,659 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + ++ secid = sepgsql_foreign_server_create(stmt->fdwname); ++ + /* + * Insert tuple into pg_foreign_server. + */ +*************** CreateForeignServer(CreateForeignServerS +*** 676,681 **** +--- 694,701 ---- + nulls[Anum_pg_foreign_server_srvoptions - 1] = true; + + tuple = heap_form_tuple(rel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, secid); + + srvId = simple_heap_insert(rel, tuple); + +*************** AlterForeignServer(AlterForeignServerStm +*** 732,737 **** +--- 752,760 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, + stmt->servername); + ++ /* SELinux permission checks */ ++ sepgsql_foreign_server_alter(srvId); ++ + memset(repl_val, 0, sizeof(repl_val)); + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); diff -Nrpc base/src/backend/commands/functioncmds.c sepgsql/src/backend/commands/functioncmds.c *** base/src/backend/commands/functioncmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/functioncmds.c Tue Sep 8 23:55:48 2009 +--- sepgsql/src/backend/commands/functioncmds.c Thu Sep 17 17:04:16 2009 *************** *** 53,58 **** --- 53,59 ---- @@ -3894,32 +4136,30 @@ diff -Nrpc base/src/backend/commands/functioncmds.c sepgsql/src/backend/commands *************** RenameFunction(List *name, List *argtype *** 1112,1117 **** ---- 1128,1138 ---- +--- 1128,1136 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux permission checks */ -+ sepgsqlCheckProcedureSetattr(procOid); -+ sepgsqlCheckSchemaRemoveName(namespaceOid); -+ sepgsqlCheckSchemaAddName(namespaceOid); ++ 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 **** ---- 1241,1248 ---- +--- 1239,1246 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(procForm->pronamespace)); } + /* SELinux permission checks */ -+ sepgsqlCheckProcedureSetattr(procOid); ++ 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 **** ---- 1281,1337 ---- +--- 1279,1337 ---- } /* @@ -3949,6 +4189,13 @@ diff -Nrpc base/src/backend/commands/functioncmds.c sepgsql/src/backend/commands + 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 */ @@ -3957,12 +4204,7 @@ diff -Nrpc base/src/backend/commands/functioncmds.c sepgsql/src/backend/commands + get_func_name(HeapTupleGetOid(newtup))); + + /* SELinux permission checks */ -+ secid = sepgsqlCheckProcedureRelabel(procOid, seclabel); -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", -+ get_func_name(procOid)))); ++ secid = sepgsql_proc_relabel(procOid, seclabel); + HeapTupleSetSecid(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); @@ -3984,40 +4226,84 @@ diff -Nrpc base/src/backend/commands/functioncmds.c sepgsql/src/backend/commands NameListToString(stmt->func->funcname)); + /* SELinux checks permissions */ -+ sepgsqlCheckProcedureSetattr(funcOid); ++ sepgsql_proc_alter(funcOid, NULL, InvalidOid); + if (procForm->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), *************** CreateCast(CreateCastStmt *stmt) -*** 1580,1585 **** ---- 1657,1665 ---- - errmsg("cast function must not return a set"))); - - ReleaseSysCache(tuple); -+ -+ /* SELinux checks db_procedure:{install} */ -+ sepgsqlCheckProcedureInstall(funcid); +*** 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; } - else - { ++ /* 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 **** ---- 1977,1987 ---- +--- 1980,1988 ---- NameStr(proc->proname), newschema))); + /* SELinux checks permissions */ -+ sepgsqlCheckProcedureSetattr(procOid); -+ sepgsqlCheckSchemaRemoveName(oldNspOid); -+ sepgsqlCheckSchemaAddName(nspOid); ++ sepgsql_proc_alter(procOid, NULL, nspOid); + /* OK, modify the pg_proc row */ /* tup is a copy, so we can scribble directly on it */ +diff -Nrpc base/src/backend/commands/indexcmds.c sepgsql/src/backend/commands/indexcmds.c +*** base/src/backend/commands/indexcmds.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/indexcmds.c Fri Sep 18 17:02:48 2009 +*************** +*** 37,42 **** +--- 37,43 ---- + #include "parser/parse_coerce.h" + #include "parser/parse_func.h" + #include "parser/parsetree.h" ++ #include "security/sepgsql.h" + #include "storage/lmgr.h" + #include "storage/proc.h" + #include "storage/procarray.h" +*************** DefineIndex(RangeVar *heapRelation, +*** 258,263 **** +--- 259,267 ---- + } + } + ++ /* SELinux checks */ ++ sepgsql_index_create(relationId, namespaceId, check_rights); ++ + /* + * look up the access method, verify it can handle the requested features + */ diff -Nrpc base/src/backend/commands/lockcmds.c sepgsql/src/backend/commands/lockcmds.c *** base/src/backend/commands/lockcmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/lockcmds.c Wed Jul 15 19:35:52 2009 +--- sepgsql/src/backend/commands/lockcmds.c Fri Sep 18 14:51:00 2009 *************** *** 20,25 **** --- 20,26 ---- @@ -4035,14 +4321,274 @@ diff -Nrpc base/src/backend/commands/lockcmds.c sepgsql/src/backend/commands/loc RelationGetRelationName(rel)))); + /* SELinux: check db_table:{lock} permission */ -+ sepgsqlCheckTableLock(reloid); ++ sepgsql_relation_lock(reloid); + /* * If requested, recurse to children. We use find_inheritance_children * not find_all_inheritors to avoid taking locks far in advance of +diff -Nrpc base/src/backend/commands/opclasscmds.c sepgsql/src/backend/commands/opclasscmds.c +*** base/src/backend/commands/opclasscmds.c Sat Jan 3 13:01:35 2009 +--- sepgsql/src/backend/commands/opclasscmds.c Thu Sep 17 17:04:16 2009 +*************** +*** 35,40 **** +--- 35,41 ---- + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +*************** CreateOpFamily(char *amname, char *opfna +*** 177,182 **** +--- 178,184 ---- + HeapTuple tup; + Datum values[Natts_pg_opfamily]; + bool nulls[Natts_pg_opfamily]; ++ Oid opfSecid; + NameData opfName; + ObjectAddress myself, + referenced; +*************** CreateOpFamily(char *amname, char *opfna +*** 197,202 **** +--- 199,207 ---- + errmsg("operator family \"%s\" for access method \"%s\" already exists", + opfname, amname))); + ++ /* SELinux check permission */ ++ opfSecid = sepgsql_opfamily_create(opfname, namespaceoid); ++ + /* + * Okay, let's create the pg_opfamily entry. + */ +*************** CreateOpFamily(char *amname, char *opfna +*** 210,215 **** +--- 215,222 ---- + values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId()); + + tup = heap_form_tuple(rel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, opfSecid); + + opfamilyoid = simple_heap_insert(rel, tup); + +*************** DefineOpClass(CreateOpClassStmt *stmt) +*** 265,270 **** +--- 272,278 ---- + Form_pg_am pg_am; + Datum values[Natts_pg_opclass]; + bool nulls[Natts_pg_opclass]; ++ Oid opcSecid; + AclResult aclresult; + NameData opcName; + ObjectAddress myself, +*************** DefineOpClass(CreateOpClassStmt *stmt) +*** 353,358 **** +--- 361,369 ---- + NameListToString(stmt->opfamilyname), stmt->amname))); + opfamilyoid = HeapTupleGetOid(tup); + ++ /* SELinux checks permission */ ++ sepgsql_opfamily_alter(opfamilyoid, NULL); ++ + /* + * XXX given the superuser check above, there's no need for an + * ownership check here +*************** DefineOpClass(CreateOpClassStmt *stmt) +*** 371,376 **** +--- 382,390 ---- + { + opfamilyoid = HeapTupleGetOid(tup); + ++ /* SELinux checks permission */ ++ sepgsql_opfamily_alter(opfamilyoid, NULL); ++ + /* + * XXX given the superuser check above, there's no need for an + * ownership check here +*************** DefineOpClass(CreateOpClassStmt *stmt) +*** 441,446 **** +--- 455,462 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); + #endif ++ /* SELinux check permission */ ++ sepgsql_opfamily_add_operator(opfamilyoid, operOid); + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); +*************** DefineOpClass(CreateOpClassStmt *stmt) +*** 465,470 **** +--- 481,488 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); + #endif ++ /* SELinux check permission */ ++ sepgsql_opfamily_add_procedure(opfamilyoid, funcOid); + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); +*************** DefineOpClass(CreateOpClassStmt *stmt) +*** 531,536 **** +--- 549,557 ---- + errmsg("operator class \"%s\" for access method \"%s\" already exists", + opcname, stmt->amname))); + ++ /* SELinux permission check */ ++ opcSecid = sepgsql_opclass_create(opcname, namespaceoid); ++ + /* + * If we are creating a default opclass, check there isn't one already. + * (Note we do not restrict this test to visible opclasses; this ensures +*************** DefineOpFamily(CreateOpFamilyStmt *stmt) +*** 657,662 **** +--- 678,684 ---- + HeapTuple tup; + Datum values[Natts_pg_opfamily]; + bool nulls[Natts_pg_opfamily]; ++ Oid opfSecid; + AclResult aclresult; + NameData opfName; + ObjectAddress myself, +*************** DefineOpFamily(CreateOpFamilyStmt *stmt) +*** 699,704 **** +--- 721,729 ---- + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to create an operator family"))); + ++ /* SELinux permission check */ ++ opfSecid = sepgsql_opfamily_create(opfname, namespaceoid); ++ + rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock); + + /* +*************** AlterOpFamily(AlterOpFamilyStmt *stmt) +*** 773,778 **** +--- 798,804 ---- + int maxOpNumber, /* amstrategies value */ + maxProcNumber; /* amsupport value */ + HeapTuple tup; ++ Oid opfSecid; + Form_pg_am pg_am; + + /* Get necessary info about access method */ +*************** AlterOpFamily(AlterOpFamilyStmt *stmt) +*** 805,810 **** +--- 831,837 ---- + errmsg("operator family \"%s\" does not exist for access method \"%s\"", + NameListToString(stmt->opfamilyname), stmt->amname))); + opfamilyoid = HeapTupleGetOid(tup); ++ opfSecid = HeapTupleGetSecid(tup); + ReleaseSysCache(tup); + + /* +*************** AlterOpFamily(AlterOpFamilyStmt *stmt) +*** 817,822 **** +--- 844,852 ---- + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to alter an operator family"))); + ++ /* SELinux permission checks */ ++ sepgsql_opfamily_alter(opfamilyoid, NULL); ++ + /* + * ADD and DROP cases need separate code from here on down. + */ +*************** AlterOpFamilyAdd(List *opfamilyname, Oid +*** 893,898 **** +--- 923,930 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); + #endif ++ /* SELinux permission check */ ++ sepgsql_opfamily_add_operator(opfamilyoid, operOid); + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); +*************** AlterOpFamilyAdd(List *opfamilyname, Oid +*** 917,922 **** +--- 949,956 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); + #endif ++ /* SELinux permission check */ ++ sepgsql_opfamily_add_procedure(opfamilyoid, funcOid); + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); +*************** RenameOpClass(List *name, const char *ac +*** 1815,1820 **** +--- 1849,1857 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux permission checks */ ++ sepgsql_opclass_alter(opcOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname); + simple_heap_update(rel, &tup->t_self, tup); +*************** RenameOpFamily(List *name, const char *a +*** 1915,1920 **** +--- 1952,1960 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux check permissions */ ++ sepgsql_opfamily_alter(opfOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); + simple_heap_update(rel, &tup->t_self, tup); +*************** AlterOpClassOwner_internal(Relation rel, +*** 2035,2040 **** +--- 2075,2082 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux permission check */ ++ sepgsql_opclass_alter(HeapTupleGetOid(tup), NULL); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +*************** AlterOpFamilyOwner_internal(Relation rel +*** 2162,2167 **** +--- 2204,2211 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux permission checks */ ++ sepgsql_opfamily_alter(HeapTupleGetOid(tup), NULL); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +diff -Nrpc base/src/backend/commands/operatorcmds.c sepgsql/src/backend/commands/operatorcmds.c +*** base/src/backend/commands/operatorcmds.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/operatorcmds.c Thu Sep 17 22:10:19 2009 +*************** +*** 45,50 **** +--- 45,51 ---- + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + #include "utils/rel.h" +*************** AlterOperatorOwner_internal(Relation rel +*** 432,437 **** +--- 433,440 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(oprForm->oprnamespace)); + } ++ /* SELinux permission check */ ++ sepgsql_operator_alter(operOid); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy diff -Nrpc base/src/backend/commands/proclang.c sepgsql/src/backend/commands/proclang.c *** base/src/backend/commands/proclang.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/proclang.c Wed Jul 15 19:48:58 2009 +--- sepgsql/src/backend/commands/proclang.c Thu Sep 17 22:10:19 2009 *************** *** 30,35 **** --- 30,36 ---- @@ -4072,7 +4618,7 @@ diff -Nrpc base/src/backend/commands/proclang.c sepgsql/src/backend/commands/pro /* *************** CreateProceduralLanguage(CreatePLangStmt -*** 186,197 **** +*** 186,192 **** NIL, PointerGetDatum(NULL), 1, @@ -4080,12 +4626,7 @@ diff -Nrpc base/src/backend/commands/proclang.c sepgsql/src/backend/commands/pro } } else - valOid = InvalidOid; - - /* ok, create it */ - create_proc_lang(languageName, GetUserId(), handlerOid, valOid, - pltemplate->tmpltrusted); ---- 188,204 ---- +--- 188,195 ---- NIL, PointerGetDatum(NULL), 1, @@ -4094,31 +4635,63 @@ diff -Nrpc base/src/backend/commands/proclang.c sepgsql/src/backend/commands/pro } } else - valOid = InvalidOid; +*************** 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 checks db_procedure:{install} */ -+ sepgsqlCheckProcedureInstall(handlerOid); -+ sepgsqlCheckProcedureInstall(valOid); + /* ++ * SELinux permission checks ++ */ ++ langSecid = sepgsql_language_create(languageName, handlerOid, valOid); + - /* ok, create it */ - create_proc_lang(languageName, GetUserId(), handlerOid, valOid, - pltemplate->tmpltrusted); -*************** CreateProceduralLanguage(CreatePLangStmt -*** 256,261 **** ---- 263,272 ---- - else - valOid = InvalidOid; ++ /* + * 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; -+ /* SELinux checks db_procedure:{install} */ -+ sepgsqlCheckProcedureInstall(handlerOid); -+ sepgsqlCheckProcedureInstall(valOid); + 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)); + - /* ok, create it */ - create_proc_lang(languageName, GetUserId(), handlerOid, valOid, - stmt->pltrusted); + /* rename */ + namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname); + simple_heap_update(rel, &tup->t_self, tup); +*************** AlterLanguageOwner_internal(HeapTuple tu +*** 613,618 **** +--- 627,635 ---- + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), newOwnerId); + ++ /* SELinux permission checks */ ++ sepgsql_language_alter(HeapTupleGetOid(tup)); ++ + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/schemacmds.c *** base/src/backend/commands/schemacmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/schemacmds.c Tue Sep 8 23:55:48 2009 +--- sepgsql/src/backend/commands/schemacmds.c Thu Sep 17 17:04:16 2009 *************** *** 25,30 **** --- 25,31 ---- @@ -4141,12 +4714,13 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s *************** CreateSchemaCommand(CreateSchemaStmt *st *** 75,80 **** ---- 77,85 ---- +--- 77,86 ---- check_is_member_of_role(saved_uid, owner_uid); + /* SELinux checks db_schema:{create} */ -+ nspsecid = sepgsqlCheckSchemaCreate(schemaName, (DefElem *)stmt->secLabel, false); ++ nspsecid = sepgsql_schema_create(schemaName, false, ++ (DefElem *)stmt->secLabel); + /* Additional check to protect reserved schema names */ if (!allowSystemTableMods && IsReservedName(schemaName)) @@ -4160,7 +4734,7 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s /* Advance cmd counter to make the namespace visible */ CommandCounterIncrement(); ---- 99,105 ---- +--- 100,106 ---- SetUserIdAndContext(owner_uid, true); /* Create the schema's namespace */ @@ -4178,7 +4752,7 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s CStringGetDatum(newname), 0, 0, 0))) ereport(ERROR, ---- 272,278 ---- +--- 273,279 ---- errmsg("schema \"%s\" does not exist", oldname))); /* make sure the new name doesn't exist */ @@ -4188,31 +4762,31 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s ereport(ERROR, *************** RenameSchema(const char *oldname, const *** 286,291 **** ---- 290,298 ---- +--- 291,299 ---- aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); + /* SELinux checks db_schema:{setattr} */ -+ sepgsqlCheckSchemaSetattr(HeapTupleGetOid(tup)); ++ sepgsql_schema_alter(HeapTupleGetOid(tup)); + if (!allowSystemTableMods && IsReservedName(newname)) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), *************** AlterSchemaOwner_internal(HeapTuple tup, *** 397,402 **** ---- 404,412 ---- +--- 405,413 ---- aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); + /* SELinux checks db_schema:{setattr} */ -+ sepgsqlCheckSchemaSetattr(HeapTupleGetOid(tup)); ++ sepgsql_schema_alter(HeapTupleGetOid(tup)); + memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); *************** AlterSchemaOwner_internal(HeapTuple tup, *** 431,433 **** ---- 441,489 ---- +--- 442,492 ---- } } @@ -4242,20 +4816,22 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s + 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 = sepgsqlCheckSchemaRelabel(HeapTupleGetOid(newtup), secLabel); -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", name))); ++ secid = sepgsql_schema_relabel(HeapTupleGetOid(newtup), secLabel); + HeapTupleSetSecid(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); ++ + CatalogUpdateIndexes(rel, newtup); + + heap_freetuple(newtup); @@ -4264,7 +4840,7 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s + } diff -Nrpc base/src/backend/commands/sequence.c sepgsql/src/backend/commands/sequence.c *** base/src/backend/commands/sequence.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/sequence.c Wed Jul 15 19:37:35 2009 +--- sepgsql/src/backend/commands/sequence.c Fri Sep 18 14:51:00 2009 *************** *** 26,31 **** --- 26,32 ---- @@ -4292,7 +4868,7 @@ diff -Nrpc base/src/backend/commands/sequence.c sepgsql/src/backend/commands/seq aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, stmt->sequence->relname); + /* SELinux checks db_sequence:{setattr} */ -+ sepgsqlCheckTableSetattr(relid); ++ sepgsql_relation_alter(relid, NULL, InvalidOid); /* do the work */ AlterSequenceInternal(relid, stmt->options); @@ -4303,7 +4879,7 @@ diff -Nrpc base/src/backend/commands/sequence.c sepgsql/src/backend/commands/seq RelationGetRelationName(seqrel)))); + /* SELinux check db_sequence:{next_value} */ -+ sepgsqlCheckSequenceNextValue(elm->relid); ++ sepgsql_sequence_next_value(elm->relid); + if (elm->last != elm->cached) /* some numbers were cached */ { @@ -4315,7 +4891,7 @@ diff -Nrpc base/src/backend/commands/sequence.c sepgsql/src/backend/commands/seq RelationGetRelationName(seqrel)))); + /* SELinux check db_sequence:{get_value} */ -+ sepgsqlCheckSequenceGetValue(elm->relid); ++ sepgsql_sequence_get_value(elm->relid); + if (!elm->last_valid) ereport(ERROR, @@ -4327,7 +4903,7 @@ diff -Nrpc base/src/backend/commands/sequence.c sepgsql/src/backend/commands/seq RelationGetRelationName(seqrel)))); + /* SELinux check db_sequence:{get_value} */ -+ sepgsqlCheckSequenceGetValue(last_used_seq->relid); ++ sepgsql_sequence_get_value(last_used_seq->relid); + result = last_used_seq->last; relation_close(seqrel, NoLock); @@ -4339,14 +4915,14 @@ diff -Nrpc base/src/backend/commands/sequence.c sepgsql/src/backend/commands/seq RelationGetRelationName(seqrel)))); + /* SELinux check db_sequence:{set_value} */ -+ sepgsqlCheckSequenceSetValue(elm->relid); ++ sepgsql_sequence_set_value(elm->relid); + /* lock page' buffer and read tuple */ seq = read_info(elm, seqrel, &buf); diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/tablecmds.c *** base/src/backend/commands/tablecmds.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/commands/tablecmds.c Tue Sep 8 23:55:48 2009 +--- sepgsql/src/backend/commands/tablecmds.c Thu Oct 8 09:29:32 2009 *************** *** 62,67 **** --- 62,68 ---- @@ -4357,6 +4933,25 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta #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 ---- @@ -4369,13 +4964,19 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta static char *validnsps[] = HEAP_RELOPT_NAMESPACES; *************** DefineRelation(CreateStmt *stmt, char re *** 444,449 **** ---- 446,455 ---- +--- 446,461 ---- localHasOids = interpretOidsOption(stmt->options); descriptor->tdhasoid = (localHasOids || parentOidCount > 0); + /* SELinux checks db_table:{create} and db_column:{create} */ -+ secLabels = sepgsqlCreateTableColumns(stmt, relname, namespaceId, -+ descriptor, relkind); ++ 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 @@ -4389,7 +4990,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta StoreCatalogInheritance(relationId, inheritOids); ---- 519,526 ---- +--- 525,532 ---- parentOidCount, stmt->oncommit, reloptions, @@ -4398,30 +4999,319 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta StoreCatalogInheritance(relationId, inheritOids); +*************** ExecuteTruncate(TruncateStmt *stmt) +*** 887,892 **** +--- 900,907 ---- + if (!pg_class_ownercheck(seq_relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(seq_rel)); ++ /* SELinux checks */ ++ sepgsql_relation_alter(seq_relid, NULL, InvalidOid); + + seq_relids = lappend_oid(seq_relids, seq_relid); + *************** truncate_check_rel(Relation rel) *** 1042,1047 **** ---- 1049,1057 ---- +--- 1057,1065 ---- errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(rel)))); + /* SELinux: check db_table:{delete} permission */ -+ sepgsqlCheckTableTruncate(rel); ++ sepgsql_relation_truncate(rel); + /* * We can never allow truncation of shared or nailed-in-cache relations, * because we can't support changing their relfilenode values. +*************** MergeAttributes(List *schema, List *supe +*** 1216,1221 **** +--- 1234,1241 ---- + if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(relation)); ++ /* SELinux checks db_table:{setattr} */ ++ sepgsql_relation_alter(RelationGetRelid(relation), NULL, InvalidOid); + + /* + * Reject duplications in the list of parents. *************** renameatt(Oid myrelid, *** 1921,1926 **** ---- 1931,1939 ---- +--- 1941,1949 ---- errmsg("cannot rename system column \"%s\"", oldattname))); + /* SELinux checks db_column:{setattr} */ -+ sepgsqlCheckColumnSetattr(myrelid, attnum); ++ sepgsql_attribute_alter(myrelid, oldattname); + /* * if the attribute is inherited, forbid the renaming, unless we are * already inside a recursive rename. +*************** RenameRelation(Oid myrelid, const char * +*** 2026,2031 **** +--- 2049,2057 ---- + Oid namespaceId; + char relkind; + ++ /* SELinux checks */ ++ sepgsql_relation_alter(myrelid, newrelname, InvalidOid); ++ + /* + * Grab an exclusive lock on the target table, index, sequence or view, + * which we will NOT release until end of transaction. +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2359,2372 **** + switch (cmd->subtype) + { + case AT_AddColumn: /* ADD COLUMN */ +! ATSimplePermissions(rel, false); + /* Performs own recursion */ + ATPrepAddColumn(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; + break; + case AT_AddColumnToView: /* add column via CREATE OR REPLACE + * VIEW */ +! ATSimplePermissions(rel, true); + /* Performs own recursion */ + ATPrepAddColumn(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; +--- 2385,2398 ---- + switch (cmd->subtype) + { + case AT_AddColumn: /* ADD COLUMN */ +! ATSimplePermissions(rel, NULL, false); + /* Performs own recursion */ + ATPrepAddColumn(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; + break; + case AT_AddColumnToView: /* add column via CREATE OR REPLACE + * VIEW */ +! ATSimplePermissions(rel, NULL, true); + /* Performs own recursion */ + ATPrepAddColumn(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2379,2397 **** + * substitutes default values into INSERTs before it expands + * rules. + */ +! ATSimplePermissions(rel, true); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP; + break; + case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ +! ATSimplePermissions(rel, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_DROP; + break; + case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ +! ATSimplePermissions(rel, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_ADD_CONSTR; +--- 2405,2423 ---- + * substitutes default values into INSERTs before it expands + * rules. + */ +! ATSimplePermissions(rel, cmd->name, true); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP; + break; + case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ +! ATSimplePermissions(rel, cmd->name, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_DROP; + break; + case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ +! ATSimplePermissions(rel, cmd->name, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_ADD_CONSTR; +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2403,2415 **** + pass = AT_PASS_COL_ATTRS; + break; + case AT_SetStorage: /* ALTER COLUMN STORAGE */ +! ATSimplePermissions(rel, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_COL_ATTRS; + break; + case AT_DropColumn: /* DROP COLUMN */ +! ATSimplePermissions(rel, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +--- 2429,2441 ---- + pass = AT_PASS_COL_ATTRS; + break; + case AT_SetStorage: /* ALTER COLUMN STORAGE */ +! ATSimplePermissions(rel, cmd->name, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_COL_ATTRS; + break; + case AT_DropColumn: /* DROP COLUMN */ +! ATSimplePermissions(rel, NULL, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2417,2429 **** + pass = AT_PASS_DROP; + break; + case AT_AddIndex: /* ADD INDEX */ +! ATSimplePermissions(rel, false); + /* This command never recurses */ + /* No command-specific prep needed */ + pass = AT_PASS_ADD_INDEX; + break; + case AT_AddConstraint: /* ADD CONSTRAINT */ +! ATSimplePermissions(rel, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +--- 2443,2455 ---- + pass = AT_PASS_DROP; + break; + case AT_AddIndex: /* ADD INDEX */ +! ATSimplePermissions(rel, NULL, false); + /* This command never recurses */ + /* No command-specific prep needed */ + pass = AT_PASS_ADD_INDEX; + break; + case AT_AddConstraint: /* ADD CONSTRAINT */ +! ATSimplePermissions(rel, NULL, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2431,2437 **** + pass = AT_PASS_ADD_CONSTR; + break; + case AT_DropConstraint: /* DROP CONSTRAINT */ +! ATSimplePermissions(rel, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +--- 2457,2463 ---- + pass = AT_PASS_ADD_CONSTR; + break; + case AT_DropConstraint: /* DROP CONSTRAINT */ +! ATSimplePermissions(rel, NULL, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2439,2445 **** + pass = AT_PASS_DROP; + break; + case AT_AlterColumnType: /* ALTER COLUMN TYPE */ +! ATSimplePermissions(rel, false); + /* Performs own recursion */ + ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd); + pass = AT_PASS_ALTER_TYPE; +--- 2465,2471 ---- + pass = AT_PASS_DROP; + break; + case AT_AlterColumnType: /* ALTER COLUMN TYPE */ +! ATSimplePermissions(rel, cmd->name, false); + /* Performs own recursion */ + ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd); + pass = AT_PASS_ALTER_TYPE; +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2451,2470 **** + break; + case AT_ClusterOn: /* CLUSTER ON */ + case AT_DropCluster: /* SET WITHOUT CLUSTER */ +! ATSimplePermissions(rel, false); + /* These commands never recurse */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; + break; + case AT_AddOids: /* SET WITH OIDS */ +! ATSimplePermissions(rel, false); + /* Performs own recursion */ + if (!rel->rd_rel->relhasoids || recursing) + ATPrepAddOids(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; + break; + case AT_DropOids: /* SET WITHOUT OIDS */ +! ATSimplePermissions(rel, false); + /* Performs own recursion */ + if (rel->rd_rel->relhasoids) + { +--- 2477,2496 ---- + break; + case AT_ClusterOn: /* CLUSTER ON */ + case AT_DropCluster: /* SET WITHOUT CLUSTER */ +! ATSimplePermissions(rel, NULL, false); + /* These commands never recurse */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; + break; + case AT_AddOids: /* SET WITH OIDS */ +! ATSimplePermissions(rel, NULL, false); + /* Performs own recursion */ + if (!rel->rd_rel->relhasoids || recursing) + ATPrepAddOids(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; + break; + case AT_DropOids: /* SET WITHOUT OIDS */ +! ATSimplePermissions(rel, NULL, false); + /* Performs own recursion */ + if (rel->rd_rel->relhasoids) + { +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2478,2491 **** + pass = AT_PASS_DROP; + break; + case AT_SetTableSpace: /* SET TABLESPACE */ +! ATSimplePermissionsRelationOrIndex(rel); + /* This command never recurses */ + ATPrepSetTableSpace(tab, rel, cmd->name); + pass = AT_PASS_MISC; /* doesn't actually matter */ + break; + case AT_SetRelOptions: /* SET (...) */ + case AT_ResetRelOptions: /* RESET (...) */ +! ATSimplePermissionsRelationOrIndex(rel); + /* This command never recurses */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; +--- 2504,2517 ---- + pass = AT_PASS_DROP; + break; + case AT_SetTableSpace: /* SET TABLESPACE */ +! ATSimplePermissionsRelationOrIndex(rel, NULL); + /* This command never recurses */ + ATPrepSetTableSpace(tab, rel, cmd->name); + pass = AT_PASS_MISC; /* doesn't actually matter */ + break; + case AT_SetRelOptions: /* SET (...) */ + case AT_ResetRelOptions: /* RESET (...) */ +! ATSimplePermissionsRelationOrIndex(rel, NULL); + /* This command never recurses */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2504,2510 **** + case AT_DisableRule: + case AT_AddInherit: /* INHERIT / NO INHERIT */ + case AT_DropInherit: +! ATSimplePermissions(rel, false); + /* These commands never recurse */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; +--- 2530,2536 ---- + case AT_DisableRule: + case AT_AddInherit: /* INHERIT / NO INHERIT */ + case AT_DropInherit: +! ATSimplePermissions(rel, NULL, false); + /* These commands never recurse */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; *************** ATRewriteTables(List **wqueue) *** 2850,2857 **** /* @@ -4432,7 +5322,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* performDeletion does CommandCounterIncrement at end */ /* ---- 2863,2871 ---- +--- 2876,2884 ---- /* * The new relation is local to our transaction and we know * nothing depends on it, so DROP_RESTRICT should be OK. @@ -4444,7 +5334,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* *************** ATRewriteTable(AlteredTableInfo *tab, Oi *** 3076,3086 **** ---- 3090,3103 ---- +--- 3103,3116 ---- if (newrel) { Oid tupOid = InvalidOid; @@ -4461,7 +5351,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta foreach(lc, dropped_attrs) *************** ATRewriteTable(AlteredTableInfo *tab, Oi *** 3112,3117 **** ---- 3129,3137 ---- +--- 3142,3150 ---- /* Preserve OID, if any */ if (newTupDesc->tdhasoid) HeapTupleSetOid(tuple, tupOid); @@ -4471,33 +5361,73 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta } /* Now check any constraints on the possibly-changed tuple */ +*************** ATGetQueueEntry(List **wqueue, Relation +*** 3213,3219 **** + * - Ensure that it is not a system table + */ + static void +! ATSimplePermissions(Relation rel, bool allowView) + { + if (rel->rd_rel->relkind != RELKIND_RELATION) + { +--- 3246,3252 ---- + * - Ensure that it is not a system table + */ + static void +! ATSimplePermissions(Relation rel, const char *colName, bool allowView) + { + if (rel->rd_rel->relkind != RELKIND_RELATION) + { *************** ATSimplePermissions(Relation rel, bool a -*** 3242,3247 **** ---- 3262,3270 ---- - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied: \"%s\" is a system catalog", - RelationGetRelationName(rel)))); -+ -+ /* SELinux checks db_table:{setattr} */ -+ sepgsqlCheckTableSetattr(RelationGetRelid(rel)); - } +*** 3237,3242 **** +--- 3270,3281 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); - /* ++ /* SELinux checks */ ++ if (!colName) ++ sepgsql_relation_alter(RelationGetRelid(rel), NULL, InvalidOid); ++ else ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + if (!allowSystemTableMods && IsSystemRelation(rel)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), +*************** ATSimplePermissions(Relation rel, bool a +*** 3252,3258 **** + * - Ensure that it is not a system table + */ + static void +! ATSimplePermissionsRelationOrIndex(Relation rel) + { + if (rel->rd_rel->relkind != RELKIND_RELATION && + rel->rd_rel->relkind != RELKIND_INDEX) +--- 3291,3297 ---- + * - Ensure that it is not a system table + */ + static void +! ATSimplePermissionsRelationOrIndex(Relation rel, const char *colName) + { + if (rel->rd_rel->relkind != RELKIND_RELATION && + rel->rd_rel->relkind != RELKIND_INDEX) *************** ATSimplePermissionsRelationOrIndex(Relat -*** 3271,3276 **** ---- 3294,3302 ---- - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied: \"%s\" is a system catalog", - RelationGetRelationName(rel)))); -+ -+ /* SELinux checks db_table:{setattr} */ -+ sepgsqlCheckTableSetattr(RelationGetRelid(rel)); - } +*** 3266,3271 **** +--- 3305,3316 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); - /* ++ /* SELinux checks */ ++ if (!colName) ++ sepgsql_relation_alter(RelationGetRelid(rel), NULL, InvalidOid); ++ else ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + if (!allowSystemTableMods && IsSystemRelation(rel)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), *************** ATExecAddColumn(AlteredTableInfo *tab, R *** 3509,3514 **** ---- 3535,3541 ---- +--- 3554,3560 ---- HeapTuple typeTuple; Oid typeOid; int32 typmod; @@ -4507,25 +5437,24 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta *************** ATExecAddColumn(AlteredTableInfo *tab, R *** 3546,3551 **** ---- 3573,3581 ---- +--- 3592,3600 ---- errmsg("child table \"%s\" has a conflicting \"%s\" column", RelationGetRelationName(rel), colDef->colname))); + /* SELinux checks db_column:{setattr} */ -+ sepgsqlCheckColumnSetattr(myrelid, childatt->attnum); ++ sepgsql_attribute_alter(myrelid, colDef->colname); + /* Bump the existing child att's inhcount */ childatt->attinhcount++; simple_heap_update(attrdesc, &tuple->t_self, tuple); *************** ATExecAddColumn(AlteredTableInfo *tab, R *** 3585,3590 **** ---- 3615,3624 ---- +--- 3634,3642 ---- errmsg("column \"%s\" of relation \"%s\" already exists", colDef->colname, RelationGetRelationName(rel)))); + /* SELinux checks db_column:{create} */ -+ attsecid = sepgsqlCheckColumnCreate(myrelid, colDef->colname, -+ (DefElem *)colDef->secLabel); ++ attsecid = sepgsql_attribute_create(myrelid, colDef); + /* Determine the new attribute's number */ if (isOid) @@ -4539,7 +5468,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta heap_close(attrdesc, RowExclusiveLock); ---- 3661,3667 ---- +--- 3679,3685 ---- ReleaseSysCache(typeTuple); @@ -4547,89 +5476,51 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta heap_close(attrdesc, RowExclusiveLock); -*************** ATExecDropNotNull(Relation rel, const ch -*** 3832,3837 **** ---- 3866,3874 ---- - errmsg("cannot alter system column \"%s\"", - colName))); - -+ /* SELinux checks db_column:{setattr} */ -+ sepgsqlCheckColumnSetattr(RelationGetRelid(rel), attnum); -+ - /* - * Check that the attribute is not in a primary key - */ -*************** ATExecSetNotNull(AlteredTableInfo *tab, -*** 3924,3929 **** ---- 3961,3969 ---- - errmsg("cannot alter system column \"%s\"", - colName))); - -+ /* SELinux checks db_column:{setattr} */ -+ sepgsqlCheckColumnSetattr(RelationGetRelid(rel), attnum); -+ - /* - * Okay, actually perform the catalog change ... if needed - */ -*************** ATExecColumnDefault(Relation rel, const -*** 3969,3974 **** ---- 4009,4017 ---- - errmsg("cannot alter system column \"%s\"", - colName))); - -+ /* SELinux checks db_column:{setattr} */ -+ sepgsqlCheckColumnSetattr(RelationGetRelid(rel), attnum); -+ - /* - * Remove any old default for the column. We use RESTRICT here for - * safety, but at present we do not expect anything to depend on the *************** ATPrepSetStatistics(Relation rel, const *** 4016,4021 **** ---- 4059,4066 ---- +--- 4068,4075 ---- if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); -+ /* SELinux checks db_table:{setatr} */ -+ sepgsqlCheckTableSetattr(RelationGetRelid(rel)); ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); } static void -*************** ATExecSetStatistics(Relation rel, const -*** 4065,4070 **** ---- 4110,4118 ---- - errmsg("cannot alter system column \"%s\"", - colName))); - -+ /* SELinux checks db_column:{setattr} */ -+ sepgsqlCheckColumnSetattr(RelationGetRelid(rel), attrtuple->attnum); -+ - attrtuple->attstattarget = newtarget; - - simple_heap_update(attrelation, &tuple->t_self, tuple); -*************** ATExecSetStorage(Relation rel, const cha -*** 4126,4131 **** ---- 4174,4182 ---- - errmsg("cannot alter system column \"%s\"", - colName))); - -+ /* SELinux checks db_column:{setattr} */ -+ sepgsqlCheckColumnSetattr(RelationGetRelid(rel), attrtuple->attnum); -+ - /* - * safety check: do not allow toasted storage modes unless column datatype - * is TOAST-aware. *************** ATExecDropColumn(List **wqueue, Relation -*** 4202,4207 **** ---- 4253,4261 ---- +*** 4171,4177 **** - ReleaseSysCache(tuple); + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +! ATSimplePermissions(rel, false); -+ /* SELinux checks db_column:{drop} */ -+ sepgsqlCheckColumnDrop(RelationGetRelid(rel), attnum); -+ /* - * Propagate to children as appropriate. Unlike most other ALTER - * routines, we have to do this one level of recursion at a time; we can't + * get the number of the attribute +--- 4225,4231 ---- + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +! ATSimplePermissions(rel, NULL, false); + + /* + * get the number of the attribute +*************** ATAddCheckConstraint(List **wqueue, Alte +*** 4473,4479 **** + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +! ATSimplePermissions(rel, false); + + /* + * Call AddRelationNewConstraints to do the work, making sure it works on +--- 4527,4533 ---- + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +! ATSimplePermissions(rel, NULL, false); + + /* + * Call AddRelationNewConstraints to do the work, making sure it works on *************** checkFkeyPermissions(Relation rel, int16 *** 5102,5108 **** aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid, @@ -4655,36 +5546,70 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta } + ok: + /* SELinux: check db_table/db_column:{reference} */ -+ sepgsqlCheckTableReference(rel, attnums, natts); ++ sepgsql_relation_references(rel, attnums, natts); } /* -*************** ATExecAlterColumnType(AlteredTableInfo * -*** 5715,5720 **** ---- 5772,5780 ---- - errmsg("cannot alter type of column \"%s\" twice", - colName))); +*************** ATExecDropConstraint(Relation rel, const +*** 5375,5381 **** + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +! ATSimplePermissions(rel, false); + + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + +--- 5432,5438 ---- + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +! ATSimplePermissions(rel, NULL, false); + + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); -+ /* SELinux checks db_column:{setattr} */ -+ sepgsqlCheckColumnSetattr(RelationGetRelid(rel), attnum); -+ - /* Look up the target type (should not fail, since prep found it) */ - typeTuple = typenameType(NULL, typename, &targettypmod); - tform = (Form_pg_type) GETSTRUCT(typeTuple); *************** ATExecChangeOwner(Oid relationOid, Oid n *** 6308,6313 **** ---- 6368,6375 ---- +--- 6365,6372 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } + /* SELinux checks db_table:{setattr} */ -+ sepgsqlCheckTableSetattr(relationOid); ++ sepgsql_relation_alter(relationOid, NULL, InvalidOid); } memset(repl_null, false, sizeof(repl_null)); +*************** ATExecAddInherit(Relation child_rel, Ran +*** 6912,6918 **** + * Must be owner of both parent and child -- child was checked by + * ATSimplePermissions call in ATPrepCmd + */ +! ATSimplePermissions(parent_rel, false); + + /* Permanent rels cannot inherit from temporary ones */ + if (parent_rel->rd_istemp && !child_rel->rd_istemp) +--- 6971,6977 ---- + * Must be owner of both parent and child -- child was checked by + * ATSimplePermissions call in ATPrepCmd + */ +! ATSimplePermissions(parent_rel, NULL, false); + + /* Permanent rels cannot inherit from temporary ones */ + if (parent_rel->rd_istemp && !child_rel->rd_istemp) +*************** AlterTableNamespace(RangeVar *relation, +*** 7570,7575 **** +--- 7629,7637 ---- + RelationGetRelationName(rel), + newschema))); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(relid, NULL, nspOid); ++ + /* disallow renaming into or out of temp schemas */ + if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid)) + ereport(ERROR, *************** AlterSeqNamespaces(Relation classRel, Re *** 7762,7767 **** ---- 7824,7951 ---- +--- 7824,7957 ---- relation_close(depRel, AccessShareLock); } @@ -4711,18 +5636,21 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta + memset(replaces, false, sizeof(replaces)); + newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), + NULL, NULL, replaces); -+ ReleaseSysCache(oldtup); -+ -+ /* SELinux checks db_table:{setattr relabelfrom relabelto} */ -+ secid = sepgsqlCheckTableRelabel(relid, seclabel); + 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); @@ -4752,18 +5680,21 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta + 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 = sepgsqlCheckColumnRelabel(relid, attnum, seclabel); -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s.%s\"", -+ get_rel_name(relid), attname))); ++ secid = sepgsql_attribute_relabel(relid, attnum, seclabel); ++ + HeapTupleSetSecid(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); ++ + CatalogUpdateIndexes(rel, newtup); + + heap_freetuple(newtup); @@ -4815,7 +5746,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta * This code supports diff -Nrpc base/src/backend/commands/trigger.c sepgsql/src/backend/commands/trigger.c *** base/src/backend/commands/trigger.c Fri Jun 19 13:40:37 2009 ---- sepgsql/src/backend/commands/trigger.c Thu Jul 16 17:22:29 2009 +--- sepgsql/src/backend/commands/trigger.c Thu Sep 17 22:10:19 2009 *************** *** 33,38 **** --- 33,39 ---- @@ -4828,19 +5759,42 @@ diff -Nrpc base/src/backend/commands/trigger.c sepgsql/src/backend/commands/trig #include "utils/acl.h" *************** CreateTrigger(CreateTrigStmt *stmt, Oid *** 182,187 **** ---- 183,191 ---- +--- 183,192 ---- NameListToString(stmt->funcname)))); } -+ /* SELinux checks db_procedure:{install} */ -+ sepgsqlCheckProcedureInstall(funcoid); ++ /* SELinux checks */ ++ if (checkPermissions) ++ sepgsql_trigger_create(RelationGetRelid(rel), stmt->trigname, funcoid); + /* * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that * references one of the built-in RI_FKey trigger functions, assume it is +*************** DropTrigger(Oid relid, const char *trign +*** 746,751 **** +--- 751,757 ---- + if (!pg_class_ownercheck(relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(relid)); ++ sepgsql_trigger_drop(relid, trigname); + + object.classId = TriggerRelationId; + object.objectId = HeapTupleGetOid(tup); +*************** renametrig(Oid relid, +*** 862,867 **** +--- 868,876 ---- + */ + targetrel = heap_open(relid, AccessExclusiveLock); + ++ /* SELinux checks */ ++ sepgsql_trigger_alter(relid, oldname); ++ + /* + * Scan pg_trigger twice for existing triggers on relation. We do this in + * order to ensure a trigger does not exist with newname (The unique index diff -Nrpc base/src/backend/commands/tsearchcmds.c sepgsql/src/backend/commands/tsearchcmds.c *** base/src/backend/commands/tsearchcmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/tsearchcmds.c Wed Jul 15 19:48:58 2009 +--- sepgsql/src/backend/commands/tsearchcmds.c Thu Sep 17 23:44:07 2009 *************** *** 35,40 **** --- 35,41 ---- @@ -4851,33 +5805,239 @@ diff -Nrpc base/src/backend/commands/tsearchcmds.c sepgsql/src/backend/commands/ #include "tsearch/ts_cache.h" #include "tsearch/ts_public.h" #include "tsearch/ts_utils.h" -*************** get_ts_parser_func(DefElem *defel, int a -*** 110,115 **** ---- 111,119 ---- - func_signature_string(funcName, nargs, typeId), - format_type_be(retTypeId)))); +*************** DefineTSParser(List *names, List *parame +*** 171,176 **** +--- 172,178 ---- + NameData pname; + Oid prsOid; + Oid namespaceoid; ++ Oid secid; -+ /* SELinux checks db_procedure:{install} */ -+ sepgsqlCheckProcedureInstall(procOid); + 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])); + - return ObjectIdGetDatum(procOid); - } + /* + * Looks good, insert + */ + prsRel = heap_open(TSParserRelationId, RowExclusiveLock); -*************** get_ts_template_func(DefElem *defel, int -*** 948,953 **** ---- 952,960 ---- - func_signature_string(funcName, nargs, typeId), - format_type_be(retTypeId)))); + tup = heap_form_tuple(prsRel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); -+ /* SELinux checks db_procedure:{install} */ -+ sepgsqlCheckProcedureInstall(procOid); + 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); + - return ObjectIdGetDatum(procOid); - } + tup = SearchSysCacheCopy(TSPARSEROID, + ObjectIdGetDatum(prsId), + 0, 0, 0); +*************** DefineTSDictionary(List *names, List *pa +*** 503,508 **** +--- 518,524 ---- + List *dictoptions = NIL; + Oid dictOid; + Oid namespaceoid; ++ Oid secid; + AclResult aclresult; + char *dictname; +*************** DefineTSDictionary(List *names, List *pa +*** 515,520 **** +--- 531,539 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceoid)); + ++ /* SELinux check */ ++ secid = sepgsql_ts_dict_create(dictname, namespaceoid); ++ + /* + * loop over the definition list and extract the information we need. + */ +*************** DefineTSDictionary(List *names, List *pa +*** 563,568 **** +--- 582,589 ---- + dictRel = heap_open(TSDictionaryRelationId, RowExclusiveLock); + + tup = heap_form_tuple(dictRel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + dictOid = simple_heap_insert(dictRel, tup); + +*************** RenameTSDictionary(List *oldname, const +*** 621,626 **** +--- 642,650 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId, newname); ++ + namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); +*************** AlterTSDictionary(AlterTSDictionaryStmt +*** 762,767 **** +--- 786,794 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, + NameListToString(stmt->dictname)); + ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId, NULL); ++ + /* deserialize the existing set of options */ + opt = SysCacheGetAttr(TSDICTOID, tup, + Anum_pg_ts_dict_dictinitoption, +*************** AlterTSDictionaryOwner(List *name, Oid n +*** 889,894 **** +--- 916,923 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId, NULL); + + form->dictowner = newOwnerId; + +*************** DefineTSTemplate(List *names, List *para +*** 999,1004 **** +--- 1028,1034 ---- + NameData dname; + int i; + Oid dictOid; ++ Oid dictSecid; + Oid namespaceoid; + char *tmplname; + +*************** DefineTSTemplate(List *names, List *para +*** 1054,1059 **** +--- 1084,1094 ---- + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("text search template lexize method is required"))); + ++ /* SELinux checks */ ++ dictSecid = sepgsql_ts_template_create(tmplname, namespaceoid, ++ DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])); ++ + /* + * Looks good, insert + */ +*************** DefineTSTemplate(List *names, List *para +*** 1061,1066 **** +--- 1096,1103 ---- + tmplRel = heap_open(TSTemplateRelationId, RowExclusiveLock); + + tup = heap_form_tuple(tmplRel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, dictSecid); + + dictOid = simple_heap_insert(tmplRel, tup); + +*************** RenameTSTemplate(List *oldname, const ch +*** 1093,1098 **** +--- 1130,1138 ---- + + tmplId = TSTemplateGetTmplid(oldname, false); + ++ /* Permission checks */ ++ sepgsql_ts_template_alter(tmplId, newname); ++ + tup = SearchSysCacheCopy(TSTEMPLATEOID, + ObjectIdGetDatum(tmplId), + 0, 0, 0); +*************** DefineTSConfiguration(List *names, List +*** 1335,1340 **** +--- 1375,1381 ---- + Oid sourceOid = InvalidOid; + Oid prsOid = InvalidOid; + Oid cfgOid; ++ Oid cfgSecid; + ListCell *pl; + + /* Convert list of names to a name and namespace */ +*************** DefineTSConfiguration(List *names, List +*** 1399,1404 **** +--- 1440,1448 ---- + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("text search parser is required"))); + ++ /* SELinux checks */ ++ cfgSecid = sepgsql_ts_config_create(cfgname, namespaceoid); ++ + /* + * Looks good, build tuple and insert + */ +*************** DefineTSConfiguration(List *names, List +*** 1414,1419 **** +--- 1458,1465 ---- + cfgRel = heap_open(TSConfigRelationId, RowExclusiveLock); + + tup = heap_form_tuple(cfgRel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, cfgSecid); + + cfgOid = simple_heap_insert(cfgRel, tup); + +*************** RenameTSConfiguration(List *oldname, con +*** 1519,1524 **** +--- 1565,1573 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* permission checks */ ++ sepgsql_ts_config_alter(cfgId, newname); ++ + namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); +*************** AlterTSConfigurationOwner(List *name, Oi +*** 1690,1695 **** +--- 1739,1746 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_ts_config_alter(cfgId, NULL); + + form->cfgowner = newOwnerId; + +*************** AlterTSConfiguration(AlterTSConfiguratio +*** 1727,1732 **** +--- 1778,1786 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, + NameListToString(stmt->cfgname)); + ++ /* SELinux checks */ ++ sepgsql_ts_config_alter(HeapTupleGetOid(tup), NULL); ++ + relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock); + + /* Add or drop mappings */ diff -Nrpc base/src/backend/commands/typecmds.c sepgsql/src/backend/commands/typecmds.c *** base/src/backend/commands/typecmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/typecmds.c Tue Sep 8 23:55:48 2009 +--- sepgsql/src/backend/commands/typecmds.c Thu Sep 17 22:10:19 2009 *************** *** 56,61 **** --- 56,62 ---- @@ -4890,106 +6050,104 @@ diff -Nrpc base/src/backend/commands/typecmds.c sepgsql/src/backend/commands/typ #include "utils/fmgroids.h" *************** AlterDomainDefault(List *names, Node *de *** 1543,1548 **** ---- 1544,1551 ---- +--- 1544,1550 ---- /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup, typename); -+ sepgsqlCheckSysobjSetattr(TypeRelationId, HeapTupleGetSecid(tup), -+ format_type_be(domainoid)); ++ sepgsql_type_alter(domainoid, NULL, InvalidOid); /* Setup new tuple */ MemSet(new_record, (Datum) 0, sizeof(new_record)); *************** AlterDomainNotNull(List *names, bool not *** 1671,1676 **** ---- 1674,1681 ---- +--- 1673,1679 ---- /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup, typename); -+ sepgsqlCheckSysobjSetattr(TypeRelationId, HeapTupleGetSecid(tup), -+ format_type_be(domainoid)); ++ 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 **** ---- 1777,1784 ---- +--- 1775,1781 ---- /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup, typename); -+ sepgsqlCheckSysobjSetattr(TypeRelationId, HeapTupleGetSecid(tup), -+ format_type_be(domainoid)); ++ 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 **** ---- 1855,1862 ---- +--- 1852,1858 ---- /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup, typename); -+ sepgsqlCheckSysobjSetattr(TypeRelationId, HeapTupleGetSecid(tup), -+ format_type_be(domainoid)); ++ sepgsql_type_alter(domainoid, NULL, InvalidOid); /* Check for unsupported constraint types */ if (IsA(newConstraint, FkConstraint)) *************** RenameType(List *names, const char *newT *** 2470,2475 **** ---- 2479,2490 ---- +--- 2475,2483 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, format_type_be(typeOid)); + /* SELinux check permission */ -+ sepgsqlCheckSysobjSetattr(TypeRelationId, HeapTupleGetSecid(tup), -+ format_type_be(typeOid)); -+ sepgsqlCheckSchemaRemoveName(typTup->typnamespace); -+ sepgsqlCheckSchemaAddName(typTup->typnamespace); ++ 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 **** ---- 2605,2613 ---- +--- 2598,2605 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(typTup->typnamespace)); } + /* SELinux checks permissions */ -+ sepgsqlCheckSysobjSetattr(TypeRelationId, HeapTupleGetSecid(tup), -+ format_type_be(HeapTupleGetOid(tup))); ++ sepgsql_type_alter(HeapTupleGetOid(tup), NULL, InvalidOid); /* * If it's a composite type, invoke ATExecChangeOwner so that we fix -*************** void -*** 2680,2685 **** ---- 2698,2704 ---- - AlterTypeNamespace(List *names, const char *newschema) - { - TypeName *typename; -+ HeapTuple typtup; - Oid typeOid; - Oid nspOid; - Oid elemOid; *************** AlterTypeNamespace(List *names, const ch *** 2706,2711 **** ---- 2725,2742 ---- +--- 2716,2724 ---- errhint("You can alter type %s, which will alter the array type as well.", format_type_be(elemOid)))); + /* SELinux checks permissions */ -+ typtup = SearchSysCache(TYPEOID, -+ ObjectIdGetDatum(typeOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(typtup)) -+ elog(ERROR, "cache lookup failed for type: %u", typeOid); -+ sepgsqlCheckSysobjSetattr(TypeRelationId, HeapTupleGetSecid(typtup), -+ format_type_be(typeOid)); -+ sepgsqlCheckSchemaRemoveName(((Form_pg_type) GETSTRUCT(typtup))->typnamespace); -+ sepgsqlCheckSchemaAddName(nspOid); -+ ReleaseSysCache(typtup); ++ sepgsql_type_alter(typeOid, NULL, nspOid); + /* and do the work */ AlterTypeNamespaceInternal(typeOid, nspOid, false, true); } +diff -Nrpc base/src/backend/commands/view.c sepgsql/src/backend/commands/view.c +*** base/src/backend/commands/view.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/view.c Fri Sep 18 14:51:00 2009 +*************** +*** 28,33 **** +--- 28,34 ---- + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteManip.h" + #include "rewrite/rewriteSupport.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +*************** DefineVirtualRelation(const RangeVar *re +*** 166,171 **** +--- 167,175 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + ++ /* SELinux checks */ ++ sepgsql_view_replace(viewOid); ++ + /* Also check it's not in use already */ + CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW"); + diff -Nrpc base/src/backend/executor/execJunk.c sepgsql/src/backend/executor/execJunk.c *** base/src/backend/executor/execJunk.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/executor/execJunk.c Wed Jul 15 19:30:50 2009 @@ -5030,7 +6188,7 @@ diff -Nrpc base/src/backend/executor/execJunk.c sepgsql/src/backend/executor/exe * Use the given slot, or make a new slot if we weren't given one. diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/execMain.c *** base/src/backend/executor/execMain.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/executor/execMain.c Wed Sep 9 16:47:01 2009 +--- sepgsql/src/backend/executor/execMain.c Thu Oct 8 09:29:32 2009 *************** *** 39,44 **** --- 39,45 ---- @@ -5330,13 +6488,17 @@ diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/exe TupleDesc tupdesc; *************** OpenIntoRel(QueryDesc *queryDesc) *** 2871,2876 **** ---- 2982,2991 ---- +--- 2982,2995 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); -+ /* SELinux checks db_table:{create} and db_column:{create} */ -+ secLabels = sepgsqlCreateTableColumns(NULL, intoName, namespaceId, -+ queryDesc->tupDesc, RELKIND_RELATION); ++ /* 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 @@ -5350,7 +6512,7 @@ diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/exe FreeTupleDesc(tupdesc); ---- 3044,3051 ---- +--- 3048,3055 ---- 0, into->onCommit, reloptions, @@ -5359,23 +6521,9 @@ diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/exe FreeTupleDesc(tupdesc); -*************** OpenIntoRel(QueryDesc *queryDesc) -*** 2956,2961 **** ---- 3072,3082 ---- - AlterTableCreateToastTable(intoRelationId, InvalidOid, reloptions, false); - - /* -+ * SELinux: checks db_table/column:{insert} permission -+ */ -+ sepgsqlCheckSelectInto(intoRelationId); -+ -+ /* - * And open the constructed table for writing. - */ - intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock); *************** intorel_receive(TupleTableSlot *slot, De *** 3054,3059 **** ---- 3175,3184 ---- +--- 3174,3183 ---- if (myState->rel->rd_rel->relhasoids) HeapTupleSetOid(tuple, InvalidOid); @@ -5388,7 +6536,7 @@ diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/exe myState->estate->es_output_cid, diff -Nrpc base/src/backend/executor/execQual.c sepgsql/src/backend/executor/execQual.c *** base/src/backend/executor/execQual.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/executor/execQual.c Tue Sep 8 17:13:41 2009 +--- sepgsql/src/backend/executor/execQual.c Thu Sep 17 17:04:16 2009 *************** *** 47,52 **** --- 47,53 ---- @@ -5405,7 +6553,7 @@ diff -Nrpc base/src/backend/executor/execQual.c sepgsql/src/backend/executor/exe aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); -+ sepgsqlCheckProcedureExecute(foid); ++ sepgsql_proc_execute(foid); /* * Safety check on nargs. Under normal circumstances this should never @@ -5415,7 +6563,7 @@ diff -Nrpc base/src/backend/executor/execQual.c sepgsql/src/backend/executor/exe if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(acoerce->elemfuncid)); -+ sepgsqlCheckProcedureExecute(acoerce->elemfuncid); ++ sepgsql_proc_execute(acoerce->elemfuncid); /* Set up the primary fmgr lookup information */ fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), @@ -5710,7 +6858,7 @@ diff -Nrpc base/src/backend/executor/functions.c sepgsql/src/backend/executor/fu Assert(tupdesc); diff -Nrpc base/src/backend/executor/nodeAgg.c sepgsql/src/backend/executor/nodeAgg.c *** base/src/backend/executor/nodeAgg.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/executor/nodeAgg.c Mon Sep 7 00:55:45 2009 +--- sepgsql/src/backend/executor/nodeAgg.c Thu Sep 17 17:04:16 2009 *************** *** 81,86 **** --- 81,87 ---- @@ -5727,7 +6875,7 @@ diff -Nrpc base/src/backend/executor/nodeAgg.c sepgsql/src/backend/executor/node if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(aggref->aggfnoid)); -+ sepgsqlCheckProcedureExecute(aggref->aggfnoid); ++ sepgsql_proc_execute(aggref->aggfnoid); peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; @@ -5737,7 +6885,7 @@ diff -Nrpc base/src/backend/executor/nodeAgg.c sepgsql/src/backend/executor/node if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(transfn_oid)); -+ sepgsqlCheckProcedureExecute(transfn_oid); ++ sepgsql_proc_execute(transfn_oid); if (OidIsValid(finalfn_oid)) { aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, @@ -5747,13 +6895,13 @@ diff -Nrpc base/src/backend/executor/nodeAgg.c sepgsql/src/backend/executor/node if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(finalfn_oid)); -+ sepgsqlCheckProcedureExecute(finalfn_oid); ++ sepgsql_proc_execute(finalfn_oid); } } diff -Nrpc base/src/backend/executor/nodeMergejoin.c sepgsql/src/backend/executor/nodeMergejoin.c *** base/src/backend/executor/nodeMergejoin.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/executor/nodeMergejoin.c Mon Sep 7 00:55:45 2009 +--- sepgsql/src/backend/executor/nodeMergejoin.c Thu Sep 17 17:04:16 2009 *************** *** 98,103 **** --- 98,104 ---- @@ -5770,7 +6918,7 @@ diff -Nrpc base/src/backend/executor/nodeMergejoin.c sepgsql/src/backend/executo if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(cmpproc)); -+ sepgsqlCheckProcedureExecute(cmpproc); ++ sepgsql_proc_execute(cmpproc); /* Set up the fmgr lookup information */ fmgr_info(cmpproc, &(clause->cmpfinfo)); @@ -5813,7 +6961,7 @@ diff -Nrpc base/src/backend/executor/nodeSubplan.c sepgsql/src/backend/executor/ sstate->projRight = ExecBuildProjectionInfo(righttlist, diff -Nrpc base/src/backend/executor/nodeWindowAgg.c sepgsql/src/backend/executor/nodeWindowAgg.c *** base/src/backend/executor/nodeWindowAgg.c Tue Jun 23 11:09:47 2009 ---- sepgsql/src/backend/executor/nodeWindowAgg.c Mon Sep 7 00:55:45 2009 +--- sepgsql/src/backend/executor/nodeWindowAgg.c Thu Sep 17 17:04:16 2009 *************** *** 43,48 **** --- 43,49 ---- @@ -5830,7 +6978,7 @@ diff -Nrpc base/src/backend/executor/nodeWindowAgg.c sepgsql/src/backend/executo if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(wfunc->winfnoid)); -+ sepgsqlCheckProcedureExecute(wfunc->winfnoid); ++ sepgsql_proc_execute(wfunc->winfnoid); /* Fill in the perfuncstate data */ perfuncstate->wfuncstate = wfuncstate; @@ -5840,7 +6988,7 @@ diff -Nrpc base/src/backend/executor/nodeWindowAgg.c sepgsql/src/backend/executo if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(transfn_oid)); -+ sepgsqlCheckProcedureExecute(transfn_oid); ++ sepgsql_proc_execute(transfn_oid); if (OidIsValid(finalfn_oid)) { aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, @@ -5850,7 +6998,7 @@ diff -Nrpc base/src/backend/executor/nodeWindowAgg.c sepgsql/src/backend/executo if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(finalfn_oid)); -+ sepgsqlCheckProcedureExecute(finalfn_oid); ++ sepgsql_proc_execute(finalfn_oid); } } @@ -5870,7 +7018,7 @@ diff -Nrpc base/src/backend/executor/spi.c sepgsql/src/backend/executor/spi.c { diff -Nrpc base/src/backend/libpq/be-fsstubs.c sepgsql/src/backend/libpq/be-fsstubs.c *** base/src/backend/libpq/be-fsstubs.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/libpq/be-fsstubs.c Wed Jul 15 19:48:58 2009 +--- sepgsql/src/backend/libpq/be-fsstubs.c Mon Sep 28 10:07:19 2009 *************** *** 42,50 **** --- 42,53 ---- @@ -5911,32 +7059,42 @@ diff -Nrpc base/src/backend/libpq/be-fsstubs.c sepgsql/src/backend/libpq/be-fsst return status; *************** lo_import_internal(text *filename, Oid l -*** 381,386 **** ---- 390,398 ---- +*** 364,369 **** +--- 373,381 ---- + * open the file to be read in */ - lobj = inv_open(oid, INV_WRITE, fscxt); - -+ /* SELinux checks db_blob:{write import} and file:{read} */ -+ sepgsqlCheckBlobImport(lobj, FileRawDescriptor(fd), fnamebuf); + text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); ++ /* SELinux checks db_blob:{write import} and file:{read} */ ++ //sepgsqlCheckBlobImport(lobj, fnamebuf); + - while ((nbytes = FileRead(fd, buf, BUFSIZE)) > 0) - { - tmp = inv_write(lobj, buf, nbytes); + fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666); + if (fd < 0) + ereport(ERROR, *************** lo_export(PG_FUNCTION_ARGS) -*** 448,453 **** ---- 460,468 ---- +*** 439,444 **** +--- 451,459 ---- + * world-writable export files doesn't seem wise. + */ + text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); ++ /* SELinux checks db_blob:{read export} and file:{write} */ ++ //sepgsqlCheckBlobExport(lobj, fnamebuf); ++ + oumask = umask((mode_t) 0022); + fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666); + umask(oumask); +*************** lo_export(PG_FUNCTION_ARGS) +*** 447,453 **** + (errcode_for_file_access(), errmsg("could not create server file \"%s\": %m", fnamebuf))); - -+ /* SELinux checks db_blob:{read export} and file:{write} */ -+ sepgsqlCheckBlobExport(lobj, FileRawDescriptor(fd), fnamebuf); -+ +- /* * read in from the inversion file and write to the filesystem */ +--- 462,467 ---- *************** lo_truncate(PG_FUNCTION_ARGS) *** 482,493 **** ---- 497,560 ---- +--- 496,559 ---- (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid large-object descriptor: %d", fd))); @@ -6207,7 +7365,7 @@ diff -Nrpc base/src/backend/optimizer/plan/createplan.c sepgsql/src/backend/opti * gating Result node that evaluates the pseudoconstants as one-time diff -Nrpc base/src/backend/optimizer/util/clauses.c sepgsql/src/backend/optimizer/util/clauses.c *** base/src/backend/optimizer/util/clauses.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/optimizer/util/clauses.c Mon Sep 7 00:55:45 2009 +--- sepgsql/src/backend/optimizer/util/clauses.c Thu Sep 17 17:04:16 2009 *************** *** 38,43 **** --- 38,44 ---- @@ -6224,7 +7382,7 @@ diff -Nrpc base/src/backend/optimizer/util/clauses.c sepgsql/src/backend/optimiz funcform->prosecdef || funcform->proretset || !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || -+ !sepgsqlHintProcedureInlined(func_tuple) || ++ !sepgsql_proc_hint_inlined(func_tuple) || funcform->pronargs != list_length(args)) return NULL; @@ -6234,7 +7392,7 @@ diff -Nrpc base/src/backend/optimizer/util/clauses.c sepgsql/src/backend/optimiz funcform->prosecdef || !funcform->proretset || !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || -+ !sepgsqlHintProcedureInlined(func_tuple) || ++ !sepgsql_proc_hint_inlined(func_tuple) || funcform->pronargs != list_length(fexpr->args)) { ReleaseSysCache(func_tuple); @@ -6812,6 +7970,30 @@ diff -Nrpc base/src/backend/parser/parse_target.c sepgsql/src/backend/parser/par /* * Check for duplicates, but only of whole columns --- we allow +diff -Nrpc base/src/backend/parser/parse_utilcmd.c sepgsql/src/backend/parser/parse_utilcmd.c +*** base/src/backend/parser/parse_utilcmd.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/parser/parse_utilcmd.c Fri Sep 18 14:51:00 2009 +*************** +*** 49,54 **** +--- 49,55 ---- + #include "parser/parse_type.h" + #include "parser/parse_utilcmd.h" + #include "rewrite/rewriteManip.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +*************** transformInhRelation(ParseState *pstate, +*** 565,570 **** +--- 566,573 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(relation)); ++ /* SELinux checks */ ++ sepgsql_relation_copy_definition(RelationGetRelid(relation)); + + tupleDesc = RelationGetDescr(relation); + constr = tupleDesc->constr; diff -Nrpc base/src/backend/postmaster/autovacuum.c sepgsql/src/backend/postmaster/autovacuum.c *** base/src/backend/postmaster/autovacuum.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/postmaster/autovacuum.c Sun Sep 6 19:53:10 2009 @@ -6834,7 +8016,7 @@ diff -Nrpc base/src/backend/postmaster/autovacuum.c sepgsql/src/backend/postmast { diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmaster/postmaster.c *** base/src/backend/postmaster/postmaster.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/postmaster/postmaster.c Sun Sep 6 19:53:10 2009 +--- sepgsql/src/backend/postmaster/postmaster.c Tue Dec 8 14:04:25 2009 *************** *** 108,113 **** --- 108,114 ---- @@ -6859,81 +8041,92 @@ diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmast PgArchPID = 0, PgStatPID = 0, ! SysLoggerPID = 0, -! sepgsqlWorkerPID = 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 **** ---- 1438,1447 ---- +--- 1439,1449 ---- if (PgStatPID == 0 && pmState == PM_RUN) PgStatPID = pgstat_start(); -+ /* If we have lost the sepgsql worker (if needed), try to start a new one */ -+ if (sepgsqlWorkerPID == 0 && pmState == PM_RUN) -+ sepgsqlWorkerPID = sepgsqlStartupWorkerProcess(); ++ /* if we have lost the selinux netlink receiver, try to start */ ++ if (sepgsqlIsEnabled() && ++ sepgsqlReceiverPID == 0 && pmState == PM_RUN) ++ sepgsqlReceiverPID = StartSELinuxReceiver(); + /* If we need to signal the autovacuum launcher, do so now */ if (avlauncher_needs_signal) { *************** SIGHUP_handler(SIGNAL_ARGS) *** 2053,2058 **** ---- 2059,2066 ---- +--- 2061,2068 ---- signal_child(SysLoggerPID, SIGHUP); if (PgStatPID != 0) signal_child(PgStatPID, SIGHUP); -+ if (sepgsqlWorkerPID != 0) -+ signal_child(sepgsqlWorkerPID, SIGHUP); ++ if (sepgsqlReceiverPID != 0) ++ signal_child(sepgsqlReceiverPID, SIGHUP); /* Reload authentication config files too */ if (!load_hba()) *************** pmdie(SIGNAL_ARGS) *** 2113,2118 **** ---- 2121,2129 ---- +--- 2123,2131 ---- /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); -+ /* and the sepgsql worker too */ -+ if (sepgsqlWorkerPID != 0) -+ signal_child(sepgsqlWorkerPID, SIGTERM); ++ /* and the selinux netlink receiver too */ ++ if (sepgsqlReceiverPID != 0) ++ signal_child(sepgsqlReceiverPID, SIGTERM); pmState = PM_WAIT_BACKUP; } *************** pmdie(SIGNAL_ARGS) *** 2160,2165 **** ---- 2171,2179 ---- +--- 2173,2181 ---- /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); -+ /* and the sepgsqlWorker too */ -+ if (sepgsqlWorkerPID != 0) -+ signal_child(sepgsqlWorkerPID, SIGTERM); ++ /* and the selinux netlink receiver too */ ++ if (sepgsqlReceiverPID != 0) ++ signal_child(sepgsqlReceiverPID, SIGTERM); pmState = PM_WAIT_BACKENDS; } *************** pmdie(SIGNAL_ARGS) *** 2193,2198 **** ---- 2207,2214 ---- +--- 2209,2216 ---- signal_child(PgArchPID, SIGQUIT); if (PgStatPID != 0) signal_child(PgStatPID, SIGQUIT); -+ if (sepgsqlWorkerPID != 0) -+ signal_child(sepgsqlWorkerPID, SIGQUIT); ++ if (sepgsqlReceiverPID != 0) ++ signal_child(sepgsqlReceiverPID, SIGQUIT); ExitPostmaster(0); break; } *************** reaper(SIGNAL_ARGS) *** 2455,2460 **** ---- 2471,2486 ---- +--- 2473,2488 ---- continue; } -+ /* Was it the sepgsql worker process? */ -+ if (pid == sepgsqlWorkerPID) ++ /* Was it the selinux netlink receiver process? */ ++ if (pid == sepgsqlReceiverPID) + { -+ sepgsqlWorkerPID = 0; ++ sepgsqlReceiverPID = 0; + if (!EXIT_STATUS_0(exitstatus)) -+ LogChildExit(LOG, _("SE-PostgreSQL worker process"), ++ LogChildExit(LOG, _("SELinux netlink receiver process"), + pid, exitstatus); + continue; + } @@ -6943,20 +8136,20 @@ diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmast */ *************** HandleChildCrash(int pid, int exitstatus *** 2646,2651 **** ---- 2672,2689 ---- +--- 2674,2691 ---- signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); } -+ /* Take care of the sepgsql worker too */ -+ if (pid == sepgsqlWorkerPID) -+ sepgsqlWorkerPID = 0; -+ else if (sepgsqlWorkerPID != 0 && !FatalError) ++ /* 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) sepgsqlWorkerPID))); -+ signal_child(sepgsqlWorkerPID, (SendStop ? SIGSTOP : SIGQUIT)); ++ (int) sepgsqlReceiverPID))); ++ signal_child(sepgsqlReceiverPID, (SendStop ? SIGSTOP : SIGQUIT)); + } + /* @@ -6971,15 +8164,80 @@ diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmast { if (FatalError) { ---- 2816,2823 ---- +--- 2818,2825 ---- StartupPID == 0 && (BgWriterPID == 0 || !FatalError) && WalWriterPID == 0 && ! AutoVacPID == 0 && -! sepgsqlWorkerPID == 0) +! sepgsqlReceiverPID == 0) { if (FatalError) { +*************** StartChildProcess(AuxProcType type) +*** 4321,4326 **** +--- 4362,4373 ---- + ereport(LOG, + (errmsg("could not fork WAL writer process: %m"))); + break; ++ #ifdef HAVE_SELINUX ++ case SelinuxReceiverProcess: ++ ereport(LOG, ++ (errmsg("could not fork selinux receiver process: %m"))); ++ break; ++ #endif + default: + ereport(LOG, + (errmsg("could not fork process: %m"))); +diff -Nrpc base/src/backend/rewrite/rewriteDefine.c sepgsql/src/backend/rewrite/rewriteDefine.c +*** base/src/backend/rewrite/rewriteDefine.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/rewrite/rewriteDefine.c Fri Sep 18 14:51:00 2009 +*************** +*** 27,32 **** +--- 27,33 ---- + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteManip.h" + #include "rewrite/rewriteSupport.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/inval.h" +*************** DefineQueryRewrite(char *rulename, +*** 266,271 **** +--- 267,275 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(event_relation)); + ++ /* SELinux checks */ ++ sepgsql_rule_create(event_relid, rulename); ++ + /* + * No rule actions that modify OLD or NEW + */ +diff -Nrpc base/src/backend/rewrite/rewriteRemove.c sepgsql/src/backend/rewrite/rewriteRemove.c +*** base/src/backend/rewrite/rewriteRemove.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/rewrite/rewriteRemove.c Fri Sep 18 14:51:00 2009 +*************** +*** 22,27 **** +--- 22,28 ---- + #include "catalog/pg_rewrite.h" + #include "miscadmin.h" + #include "rewrite/rewriteRemove.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/fmgroids.h" + #include "utils/inval.h" +*************** RemoveRewriteRule(Oid owningRel, const c +*** 78,83 **** +--- 79,87 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(eventRelationOid)); + ++ /* SELinux checks */ ++ sepgsql_rule_drop(eventRelationOid, ruleName); ++ + /* + * Do the deletion + */ diff -Nrpc base/src/backend/security/Makefile sepgsql/src/backend/security/Makefile *** base/src/backend/security/Makefile Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/Makefile Wed Jul 15 19:39:56 2009 @@ -7128,7 +8386,7 @@ diff -Nrpc base/src/backend/security/rowlevel.c sepgsql/src/backend/security/row + } diff -Nrpc base/src/backend/security/sepgsql/Makefile sepgsql/src/backend/security/sepgsql/Makefile *** base/src/backend/security/sepgsql/Makefile Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/Makefile Thu Jul 16 17:22:29 2009 +--- sepgsql/src/backend/security/sepgsql/Makefile Thu Sep 17 17:04:16 2009 *************** *** 0 **** --- 1,16 ---- @@ -7142,7 +8400,7 @@ diff -Nrpc base/src/backend/security/sepgsql/Makefile sepgsql/src/backend/securi + include $(top_builddir)/src/Makefile.global + + ifeq ($(enable_selinux), yes) -+ OBJS = avc.o checker.o hooks.o label.o misc.o perms.o ++ OBJS = avc.o checker.o bridge.o hooks.o label.o misc.o perms.o + else + OBJS = dummy.o + endif @@ -7150,10 +8408,10 @@ diff -Nrpc base/src/backend/security/sepgsql/Makefile sepgsql/src/backend/securi + include $(top_srcdir)/src/backend/common.mk diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/sepgsql/avc.c *** base/src/backend/security/sepgsql/avc.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/avc.c Wed Sep 9 17:45:12 2009 +--- sepgsql/src/backend/security/sepgsql/avc.c Tue Dec 8 14:16:15 2009 *************** *** 0 **** ---- 1,894 ---- +--- 1,880 ---- + /* + * src/backend/security/sepgsql/avc.c + * SE-PostgreSQL userspace access vector cache @@ -7952,7 +9210,7 @@ diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/ + if (c) + *c = '\0'; + -+ ereport(LOG, ++ ereport(LOG, + (errcode(ERRCODE_SELINUX_INFO), + errmsg("%s", buffer))); + @@ -7964,8 +9222,8 @@ diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/ + { + /* switch enforcing/permissive */ + LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); -+ selinux_state->version = selinux_state->version + 1; + selinux_state->enforcing = (enforce ? true : false); ++ selinux_state->version++; + LWLockRelease(SepgsqlAvcLock); + + return 0; @@ -7976,25 +9234,23 @@ diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/ + { + /* invalidate local avc */ + LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); -+ selinux_state->version = selinux_state->version + 1; ++ selinux_state->version++; + LWLockRelease(SepgsqlAvcLock); + + return 0; + } + -+ static int -+ sepgsqlWorkerMain(void) ++ void ++ sepgsqlReceiverMain(void) + { + union selinux_callback cb; + -+ ClosePostmasterPorts(false); ++ Assert(sepgsqlIsEnabled()); + -+ on_exit_reset(); -+ -+ /* -+ * map shared memory segment -+ */ -+ sepgsqlShmemInit(); ++ #ifdef HAVE_SETSID ++ if (setsid() < 0) ++ elog(FATAL, "setsid() failed: %m"); ++ #endif + + /* + * setup the signal handler @@ -8009,6 +9265,11 @@ diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/ + 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()))); @@ -8029,31 +9290,2896 @@ diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/ + + avc_netlink_loop(); + -+ return 0; ++ exit(0); + } -+ -+ pid_t -+ sepgsqlStartupWorkerProcess(void) -+ { -+ pid_t chld; -+ -+ if (!sepgsqlIsEnabled()) -+ return (pid_t) 0; -+ -+ chld = fork(); -+ if (chld == 0) -+ exit(sepgsqlWorkerMain()); -+ else if (chld > 0) -+ return chld; -+ -+ return (pid_t) 0; -+ } -diff -Nrpc base/src/backend/security/sepgsql/checker.c sepgsql/src/backend/security/sepgsql/checker.c -*** base/src/backend/security/sepgsql/checker.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/checker.c Thu Sep 10 00:06:47 2009 +diff -Nrpc base/src/backend/security/sepgsql/bridge.c sepgsql/src/backend/security/sepgsql/bridge.c +*** base/src/backend/security/sepgsql/bridge.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/bridge.c Tue Dec 1 17:11:40 2009 *************** *** 0 **** ---- 1,443 ---- +--- 1,2876 ---- ++ /* ++ * src/backend/security/sepgsql/bridge.c ++ * ++ * New style security hooks for SE-PostgreSQL ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++ #include "postgres.h" ++ ++ #include "access/sysattr.h" ++ #include "catalog/heap.h" ++ #include "catalog/indexing.h" ++ #include "catalog/pg_authid.h" ++ #include "catalog/pg_cast.h" ++ #include "catalog/pg_conversion.h" ++ #include "catalog/pg_database.h" ++ #include "catalog/pg_foreign_data_wrapper.h" ++ #include "catalog/pg_foreign_server.h" ++ #include "catalog/pg_language.h" ++ #include "catalog/pg_namespace.h" ++ #include "catalog/pg_operator.h" ++ #include "catalog/pg_opclass.h" ++ #include "catalog/pg_opfamily.h" ++ #include "catalog/pg_proc.h" ++ #include "catalog/pg_rewrite.h" ++ #include "catalog/pg_security.h" ++ #include "catalog/pg_tablespace.h" ++ #include "catalog/pg_ts_parser.h" ++ #include "catalog/pg_ts_dict.h" ++ #include "catalog/pg_ts_template.h" ++ #include "catalog/pg_ts_config.h" ++ #include "catalog/pg_type.h" ++ #include "catalog/pg_user_mapping.h" ++ #include "commands/dbcommands.h" ++ #include "miscadmin.h" ++ #include "security/sepgsql.h" ++ #include "utils/builtins.h" ++ #include "utils/fmgroids.h" ++ #include "utils/lsyscache.h" ++ #include "utils/syscache.h" ++ #include "utils/tqual.h" ++ ++ #include ++ ++ /* ------------------------------------------------------------ * ++ * Common Helper Routines ++ * ------------------------------------------------------------ */ ++ static bool sepgsql_database_common(Oid datOid, uint32 required, bool abort); ++ static bool sepgsql_schema_common(Oid nspOid, uint32 required, bool abort); ++ static bool sepgsql_attribute_common(Oid relOid, AttrNumber attnum, ++ uint32 required, bool abort); ++ static bool sepgsql_relation_common(Oid relOid, uint32 required, bool abort); ++ static bool sepgsql_proc_common(Oid procOid, uint32 required, bool abort); ++ static bool sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort); ++ static bool sepgsql_foreign_server_common(Oid fsrvOid, uint32 required, bool abort); ++ static bool sepgsql_language_common(Oid langOid, uint32 required, bool abort); ++ static bool sepgsql_operator_common(Oid oprOid, uint32 required, bool abort); ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_database related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ static bool ++ sepgsql_database_common(Oid datOid, uint32 required, bool abort) ++ { ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ const char *auname; ++ bool rc; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(datOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for database: %u", datOid); ++ ++ auname = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); ++ ++ sid = sepgsqlGetTupleSecid(DatabaseRelationId, tuple, &tclass); ++ ++ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); ++ ++ ReleaseSysCache(tuple); ++ ++ return rc; ++ } ++ ++ Oid ++ sepgsql_database_create(const char *datName, DefElem *newLabel) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ if (!newLabel) ++ sid = sepgsqlGetDefaultDatabaseSecid(); ++ else ++ { ++ sid.relid = DatabaseRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, ++ strVal(newLabel->arg)); ++ } ++ ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_DATABASE, ++ SEPG_DB_DATABASE__CREATE, ++ datName, true); ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_database_alter(Oid datOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_database_common(datOid, SEPG_DB_DATABASE__SETATTR, true); ++ } ++ ++ void ++ sepgsql_database_drop(Oid datOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_database_common(datOid, SEPG_DB_DATABASE__DROP, true); ++ } ++ ++ Oid ++ sepgsql_database_relabel(Oid datOid, DefElem *newLabel) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ { ++ if (newLabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux is disabled now"))); ++ ++ return InvalidOid; ++ } ++ sid.relid = DatabaseRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ ++ /* db_database:{setattr relabelfrom} to older seclabel */ ++ sepgsql_database_common(datOid, ++ SEPG_DB_DATABASE__SETATTR | ++ SEPG_DB_DATABASE__RELABELFROM, true); ++ ++ /* db_database:{relabelto} to newer seclabel */ ++ sepgsqlClientHasPerms(sid, ++ SEPG_CLASS_DB_DATABASE, ++ SEPG_DB_DATABASE__RELABELTO, ++ get_database_name(datOid), true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_database_grant(Oid datOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_database_common(datOid, SEPG_DB_DATABASE__SETATTR, true); ++ } ++ ++ void ++ sepgsql_database_access(Oid datOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_database_common(datOid, SEPG_DB_DATABASE__ACCESS, true); ++ } ++ ++ bool ++ sepgsql_database_superuser(Oid datOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return true; ++ ++ return sepgsql_database_common(datOid, SEPG_DB_DATABASE__SUPERUSER, false); ++ } ++ ++ void ++ sepgsql_database_load_module(Oid datOid, const char *filename) ++ { ++ HeapTuple tuple; ++ security_context_t filecon; ++ security_context_t datcon; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ /* ++ * It assumes preloaded libraries are secure, ++ * because it can be set up using guc variable ++ * not any SQL statements. ++ */ ++ if (GetProcessingMode() == InitProcessing) ++ return; ++ ++ /* Get database context */ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(datOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for database: %u", datOid); ++ ++ datcon = securityRawSecLabelOut(DatabaseRelationId, ++ HeapTupleGetSecid(tuple)); ++ ReleaseSysCache(tuple); ++ ++ /* Get library context */ ++ if (getfilecon_raw(filename, &filecon) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not access file \"%s\": %m", filename))); ++ PG_TRY(); ++ { ++ sepgsqlComputePerms(datcon, ++ filecon, ++ SEPG_CLASS_DB_DATABASE, ++ SEPG_DB_DATABASE__LOAD_MODULE, ++ filename, true); ++ } ++ PG_CATCH(); ++ { ++ freecon(filecon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(filecon); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_namespace related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ static bool ++ sepgsql_schema_common(Oid nspOid, uint32 required, bool abort) ++ { ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ const char *auname; ++ bool rc; ++ ++ tuple = SearchSysCache(NAMESPACEOID, ++ ObjectIdGetDatum(nspOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for namespace: %u", nspOid); ++ ++ sid = sepgsqlGetTupleSecid(NamespaceRelationId, tuple, &tclass); ++ ++ auname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); ++ ++ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); ++ ++ ReleaseSysCache(tuple); ++ ++ return rc; ++ } ++ ++ Oid ++ sepgsql_schema_create(const char *nspName, bool isTemp, DefElem *newLabel) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ if (!newLabel) ++ sid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); ++ else ++ { ++ sid.relid = NamespaceRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ } ++ ++ sepgsqlClientHasPerms(sid, ++ SEPG_CLASS_DB_SCHEMA, ++ SEPG_DB_SCHEMA__CREATE, ++ nspName, true); ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_schema_alter(Oid nspOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SETATTR, true); ++ } ++ ++ void ++ sepgsql_schema_drop(Oid nspOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__DROP, true); ++ } ++ ++ Oid ++ sepgsql_schema_relabel(Oid nspOid, DefElem *newLabel) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ { ++ if (newLabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux is disabled now"))); ++ return InvalidOid; ++ } ++ sid.relid = NamespaceRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ ++ /* db_schema:{setattr relabelfrom} for older seclabel */ ++ sepgsql_schema_common(nspOid, ++ SEPG_DB_SCHEMA__SETATTR | ++ SEPG_DB_SCHEMA__RELABELFROM, true); ++ ++ /* db_schema:{relabelto} for newer seclabel */ ++ sepgsqlClientHasPerms(sid, ++ SEPG_CLASS_DB_SCHEMA, ++ SEPG_DB_SCHEMA__RELABELTO, ++ get_namespace_name(nspOid), true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_schema_grant(Oid nspOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SETATTR, true); ++ } ++ ++ bool ++ sepgsql_schema_search(Oid nspOid, bool abort) ++ { ++ if (!sepgsqlIsEnabled()) ++ return true; ++ ++ return sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SEARCH, abort); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_attribute related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ static bool ++ sepgsql_attribute_common(Oid relOid, AttrNumber attnum, ++ uint32 required, bool abort) ++ { ++ Form_pg_attribute attForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ char auname[NAMEDATALEN * 2 + 3]; ++ bool rc = true; ++ ++ /* Caller prevent case when relkind != RELKIND_RELATION */ ++ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); ++ ++ tuple = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(relOid), ++ Int16GetDatum(attnum), ++ 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for attribute %d of relation %u", ++ attnum, relOid); ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ ++ /* ++ * NOTE: when a table to be dropped, corresponding attributes ++ * are also removed. Some of them can be already logically ++ * dropped using ALTER TABLE ... DROP statement. ++ * In this case, SE-PostgreSQL does not check anything. ++ * If any other situation touches dropped column, it is a bug. ++ */ ++ if (attForm->attisdropped) ++ goto skip; ++ ++ sprintf(auname, "%s.%s", get_rel_name(relOid), NameStr(attForm->attname)); ++ ++ sid = sepgsqlGetTupleSecid(AttributeRelationId, tuple, &tclass); ++ ++ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); ++ ++ skip: ++ ReleaseSysCache(tuple); ++ ++ return rc; ++ } ++ ++ Oid ++ sepgsql_attribute_create(Oid relOid, ColumnDef *cdef) ++ { ++ sepgsql_sid_t sid; ++ char relkind; ++ ++ if (!sepgsqlIsEnabled()) ++ { ++ if (cdef->secLabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux is disabled now"))); ++ return InvalidOid; ++ } ++ ++ relkind = get_rel_relkind(relOid); ++ if (relkind == RELKIND_RELATION) ++ { ++ char auname[NAMEDATALEN * 2 + 3]; ++ ++ if (!cdef->secLabel) ++ sid = sepgsqlGetDefaultColumnSecid(relOid); ++ else ++ { ++ char *label = strVal(((DefElem *)cdef->secLabel)->arg); ++ ++ sid.relid = AttributeRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, label); ++ } ++ ++ sprintf(auname, "%s.%s", get_rel_name(relOid), cdef->colname); ++ sepgsqlClientHasPerms(sid, ++ SEPG_CLASS_DB_COLUMN, ++ SEPG_DB_COLUMN__CREATE, ++ auname, true); ++ } ++ else ++ { ++ /* no need to check for toast relation */ ++ if (relkind != RELKIND_TOASTVALUE) ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ return InvalidOid; ++ } ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_attribute_alter(Oid relOid, const char *attname) ++ { ++ AttrNumber attno; ++ char relkind; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* ++ * If the target attribute does not exist, an error ++ * shall be raised later. ++ */ ++ attno = get_attnum(relOid, attname); ++ if (attno == InvalidAttrNumber) ++ return; ++ ++ relkind = get_rel_relkind(relOid); ++ if (relkind == RELKIND_RELATION) ++ { ++ sepgsql_attribute_common(relOid, attno, SEPG_DB_COLUMN__SETATTR, true); ++ } ++ else if (relkind != RELKIND_TOASTVALUE) ++ { ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ } ++ } ++ ++ void ++ sepgsql_attribute_drop(Oid relOid, AttrNumber attnum) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* ++ * We only need to check db_column:{drop} when relkind equals ++ * RELKIND_RELATION, because db_xxx:{drop} permission is already ++ * checked in other cases. (e.g DROP SEQUENCE, ...) ++ */ ++ if (get_rel_relkind(relOid) == RELKIND_RELATION) ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__DROP, true); ++ } ++ ++ void ++ sepgsql_attribute_grant(Oid relOid, AttrNumber attnum) ++ { ++ char relkind; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ relkind = get_rel_relkind(relOid); ++ if (relkind == RELKIND_RELATION) ++ { ++ sepgsql_attribute_common(relOid, attnum, SEPG_DB_COLUMN__SETATTR, true); ++ } ++ else if (relkind != RELKIND_TOASTVALUE) ++ { ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ } ++ } ++ ++ Oid ++ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, DefElem *newLabel) ++ { ++ sepgsql_sid_t sid; ++ char auname[NAMEDATALEN * 2 + 3]; ++ ++ if (!sepgsqlIsEnabled()) ++ { ++ if (!newLabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux is disabled now"))); ++ return InvalidOid; ++ } ++ ++ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); ++ ++ sid.relid = AttributeRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ ++ /* db_column:{setattr relabelfrom} */ ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__SETATTR | ++ SEPG_DB_COLUMN__RELABELFROM, true); ++ ++ /* db_column:{relabelto} */ ++ sprintf(auname, "%s.%s", ++ get_rel_name(relOid), ++ get_attname(relOid, attnum)); ++ sepgsqlClientHasPerms(sid, ++ SEPG_CLASS_DB_COLUMN, ++ SEPG_DB_COLUMN__RELABELTO, ++ auname, true); ++ ++ return sid.secid; ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_class related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ static bool ++ sepgsql_relation_common(Oid relOid, uint32 required, bool abort) ++ { ++ Form_pg_class relForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ bool rc; ++ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relOid); ++ relForm = (Form_pg_class) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(RelationRelationId, tuple, &tclass); ++ rc = sepgsqlClientHasPerms(sid, tclass, required, ++ NameStr(relForm->relname), abort); ++ ++ ReleaseSysCache(tuple); ++ ++ return rc; ++ } ++ ++ /* ++ * sepgsql_relation_create ++ * It returns an array of security identifier for the new table ++ * and columns to be assigned. The corresponding security labels ++ * are already checked for db_table/db_sequence/db_column:{create} ++ * permission. ++ * In the default labeling rule, a column inherits the security ++ * label of its table, but we cannot refer it using system caches, ++ * because the command counter is not incremented under the ++ * heap_create_with_catalog(). Thus, we need to compute and check ++ * them prior to the actual creation of table and columns. ++ */ ++ Oid * ++ sepgsql_relation_create(const char *relName, ++ char relkind, ++ TupleDesc tupDesc, ++ Oid nspOid, ++ DefElem *relLabel, ++ List *colList, ++ bool createAs, ++ bool permission) ++ { ++ Oid *secLabels; ++ sepgsql_sid_t relsid; ++ uint16 tclass; ++ uint32 required; ++ int index; ++ ++ if (!sepgsqlIsEnabled()) ++ return NULL; ++ ++ switch (relkind) ++ { ++ case RELKIND_RELATION: ++ if (!relLabel) ++ relsid = sepgsqlGetDefaultTableSecid(nspOid); ++ else ++ { ++ relsid.relid = RelationRelationId; ++ relsid.secid = securityTransSecLabelIn(relsid.relid, ++ strVal(relLabel->arg)); ++ } ++ tclass = SEPG_CLASS_DB_TABLE; ++ required = SEPG_DB_TABLE__CREATE; ++ if (createAs) ++ required |= SEPG_DB_TABLE__INSERT; ++ break; ++ ++ case RELKIND_SEQUENCE: ++ if (!relLabel) ++ relsid = sepgsqlGetDefaultSequenceSecid(nspOid); ++ else ++ { ++ relsid.relid = RelationRelationId; ++ relsid.secid = securityTransSecLabelIn(relsid.relid, ++ strVal(relLabel->arg)); ++ } ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ required = SEPG_DB_SEQUENCE__CREATE; ++ break; ++ ++ default: ++ if (!relLabel) ++ relsid = sepgsqlGetDefaultTupleSecid(RelationRelationId); ++ else ++ { ++ /* should not be happen */ ++ relsid.relid = RelationRelationId; ++ relsid.secid = securityTransSecLabelIn(relsid.relid, ++ strVal(relLabel->arg)); ++ } ++ tclass = SEPG_CLASS_DB_TUPLE; ++ required = SEPG_DB_TUPLE__INSERT; ++ break; ++ } ++ ++ /* ++ * The secLabeld array stores security identifiers to be assigned ++ * on the new table and columns. ++ * ++ * secLabels[0] is security identifier of the table. ++ * secLabels[attnum - FirstLowInvalidHeapAttributeNumber] ++ * is security identifier of columns (if necessary). ++ */ ++ secLabels = palloc0(sizeof(Oid) * (tupDesc->natts ++ - FirstLowInvalidHeapAttributeNumber)); ++ ++ /* relation's security identifier to be assigned on */ ++ secLabels[0] = relsid.secid; ++ ++ /* ++ * Note that this hook can be called during initdb processes. ++ * It is an exception of access controls, so we skip any checks. ++ * ++ * And, we don't need any checks for toast relations, because ++ * it is a quite internal stuff. ++ */ ++ if (permission) ++ { ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_table:{create}, db_sequence:{create} or db_tuple:{insert} */ ++ sepgsqlClientHasPerms(relsid, tclass, required, relName, true); ++ } ++ ++ /* no individual security context expect for RELKIND_RELATION */ ++ if (relkind != RELKIND_RELATION) ++ return secLabels; ++ ++ /* ++ * db_column:{create} permission ++ */ ++ for (index = FirstLowInvalidHeapAttributeNumber + 1; ++ index < tupDesc->natts; ++ index++) ++ { ++ Form_pg_attribute attr; ++ sepgsql_sid_t attsid = { InvalidOid, InvalidOid }; ++ char attname[NAMEDATALEN * 2 + 3]; ++ ListCell *l; ++ ++ /* skip unnecessary attributes */ ++ if (index == ObjectIdAttributeNumber && !tupDesc->tdhasoid) ++ continue; ++ ++ if (index < 0) ++ attr = SystemAttributeDefinition(index, tupDesc->tdhasoid); ++ else ++ attr = tupDesc->attrs[index]; ++ ++ /* Is there any given security context? */ ++ foreach (l, colList) ++ { ++ ColumnDef *cdef = lfirst(l); ++ ++ if (cdef->secLabel && ++ strcmp(cdef->colname, NameStr(attr->attname)) == 0) ++ { ++ attsid.relid = AttributeRelationId; ++ attsid.secid = securityTransSecLabelIn(attsid.relid, ++ strVal(((DefElem *)cdef->secLabel)->arg)); ++ break; ++ } ++ } ++ ++ /* default security context, if not given */ ++ if (!SidIsValid(attsid)) ++ attsid = sepgsqlClientCreateSecid(relsid, ++ SEPG_CLASS_DB_COLUMN, ++ AttributeRelationId); ++ if (permission) ++ { ++ required = SEPG_DB_COLUMN__CREATE; ++ ++ if (createAs) ++ required |= SEPG_DB_COLUMN__INSERT; ++ ++ /* db_column:{create (insert)} */ ++ sprintf(attname, "%s.%s", relName, NameStr(attr->attname)); ++ sepgsqlClientHasPerms(attsid, ++ SEPG_CLASS_DB_COLUMN, ++ required, attname, true); ++ } ++ /* column's security identifier to be assigend on */ ++ secLabels[index - FirstLowInvalidHeapAttributeNumber] = attsid.secid; ++ } ++ ++ return secLabels; ++ } ++ ++ /* ++ * sepgsql_relation_copy ++ * It returns an array of security identifier of table and columns ++ * to be copied on make_new_heap(). It actually create a new temporary ++ * relation and insert all the tuples within original one into the ++ * temporary one, but swap_relation_files() swaps their file nodes. ++ * Thus, there are no changes from the viewpoint of users. ++ * SE-PostgreSQL also does not check and change anything. It simply ++ * copies security identifier of the source relation to the destination ++ * relation. ++ */ ++ Oid * ++ sepgsql_relation_copy(Relation src) ++ { ++ Oid *secLabels; ++ HeapTuple tuple; ++ Oid relOid = RelationGetRelid(src); ++ int index; ++ ++ if (!sepgsqlIsEnabled()) ++ return NULL; ++ ++ /* see the comment at sepgsqlCreateTableColumn*/ ++ secLabels = palloc0(sizeof(Oid) * (RelationGetDescr(src)->natts ++ - FirstLowInvalidHeapAttributeNumber)); ++ ++ /* copy table's security identifier */ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation \"%s\"", ++ RelationGetRelationName(src)); ++ ++ secLabels[0] = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ ++ /* copy column's security identifier */ ++ for (index = FirstLowInvalidHeapAttributeNumber + 1; ++ index < RelationGetDescr(src)->natts; ++ index++) ++ { ++ Form_pg_attribute attr; ++ ++ if (index < 0) ++ attr = SystemAttributeDefinition(index, true); ++ else ++ attr = RelationGetDescr(src)->attrs[index]; ++ ++ tuple = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(relOid), ++ Int16GetDatum(attr->attnum), ++ 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ continue; ++ ++ secLabels[index - FirstLowInvalidHeapAttributeNumber] ++ = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ return secLabels; ++ } ++ ++ void ++ sepgsql_relation_alter(Oid relOid, const char *newName, Oid newNsp) ++ { ++ Form_pg_class relForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relOid); ++ relForm = (Form_pg_class) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(RelationRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TABLE__SETATTR, ++ NameStr(relForm->relname), true); ++ ++ /* db_schema:{add_name remove_name}, if necessary */ ++ if (newName || OidIsValid(newNsp)) ++ { ++ if (!OidIsValid(newNsp)) ++ sepgsql_schema_common(relForm->relnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ else ++ { ++ sepgsql_schema_common(relForm->relnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); ++ } ++ } ++ ReleaseSysCache(tuple); ++ } ++ ++ void ++ sepgsql_relation_drop(Oid relOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ if (get_rel_relkind(relOid) == RELKIND_TOASTVALUE) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__DROP, true); ++ } ++ ++ void ++ sepgsql_relation_grant(Oid relOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ } ++ ++ Oid ++ sepgsql_relation_relabel(Oid relOid, DefElem *newLabel) ++ { ++ sepgsql_sid_t sid; ++ char relkind; ++ ++ if (!sepgsqlIsEnabled()) ++ { ++ if (newLabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux is disabled now"))); ++ return InvalidOid; ++ } ++ ++ relkind = get_rel_relkind(relOid); ++ if (relkind != RELKIND_RELATION && relkind != RELKIND_SEQUENCE) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set security label on \"%s\"", ++ get_rel_name(relOid)))); ++ ++ /* input security context */ ++ sid.relid = RelationRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ ++ /* db_table/db_sequence:{setattr relabelfrom} */ ++ sepgsql_relation_common(relOid, ++ SEPG_DB_TABLE__SETATTR | ++ SEPG_DB_TABLE__RELABELFROM, true); ++ ++ /* db_table/db_sequence:{relabelto} */ ++ sepgsqlClientHasPerms(sid, ++ (relkind == RELKIND_RELATION ++ ? SEPG_CLASS_DB_TABLE ++ : SEPG_CLASS_DB_SEQUENCE), ++ SEPG_DB_TABLE__RELABELTO, ++ get_rel_name(relOid), true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_relation_get_transaction_id(Oid relOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__GETATTR, true); ++ } ++ ++ void ++ sepgsql_relation_copy_definition(Oid relOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__GETATTR, true); ++ } ++ ++ void ++ sepgsql_relation_truncate(Relation rel) ++ { ++ HeapScanDesc scan; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* check db_table:{delete} permission */ ++ sepgsql_relation_common(RelationGetRelid(rel), ++ SEPG_DB_TABLE__DELETE, true); ++ ++ /* row-level access control is enabled? */ ++ if (!sepostgresql_row_level) ++ return; ++ ++ /* check db_tuple:{delete} permission */ ++ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); ++ ++ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) ++ { ++ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NULL, true); ++ } ++ heap_endscan(scan); ++ } ++ ++ void ++ sepgsql_relation_references(Relation rel, int16 *attnums, int natts) ++ { ++ Oid relOid = RelationGetRelid(rel); ++ int i; ++ ++ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* db_table:{reference} */ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__REFERENCE, true); ++ ++ for (i=0; i < natts; i++) ++ sepgsql_attribute_common(relOid, attnums[i], ++ SEPG_DB_COLUMN__REFERENCE, true); ++ } ++ ++ void ++ sepgsql_relation_lock(Oid relOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ if (get_rel_relkind(relOid) != RELKIND_RELATION) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__LOCK, true); ++ } ++ ++ void ++ sepgsql_view_replace(Oid viewOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ Assert(get_rel_relkind(viewOid) == RELKIND_VIEW); ++ ++ sepgsql_relation_common(viewOid, SEPG_DB_TABLE__SETATTR, true); ++ } ++ ++ void ++ sepgsql_index_create(Oid relOid, Oid nspOid, bool check_rights) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ if (check_rights) ++ { ++ /* db_table:{setattr} */ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ } ++ } ++ ++ void ++ sepgsql_sequence_get_value(Oid seqOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); ++ ++ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__GET_VALUE, true); ++ } ++ ++ void ++ sepgsql_sequence_next_value(Oid seqOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); ++ ++ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__NEXT_VALUE, true); ++ } ++ ++ void ++ sepgsql_sequence_set_value(Oid seqOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); ++ ++ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__SET_VALUE, true); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_proc related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ static bool ++ sepgsql_proc_common(Oid procOid, uint32 required, bool abort) ++ { ++ sepgsql_sid_t sid; ++ HeapTuple tuple; ++ uint16 tclass; ++ const char *auname; ++ bool rc; ++ ++ tuple = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(procOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for procedure: %u", procOid); ++ ++ auname = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); ++ sid = sepgsqlGetTupleSecid(ProcedureRelationId, tuple, &tclass); ++ ++ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); ++ ++ ReleaseSysCache(tuple); ++ ++ return rc; ++ } ++ ++ Oid ++ sepgsql_proc_create(const char *procName, HeapTuple oldTup, ++ Oid nspOid, Oid langOid, DefElem *newLabel) ++ { ++ sepgsql_sid_t sid; ++ HeapTuple tuple; ++ uint32 required; ++ bool trusted; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ if (!HeapTupleIsValid(oldTup)) ++ { ++ /* create a new function */ ++ required = SEPG_DB_PROCEDURE__CREATE; ++ if (!newLabel) ++ sid = sepgsqlGetDefaultProcedureSecid(nspOid); ++ else ++ { ++ sid.relid = ProcedureRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ } ++ } ++ else if (!newLabel) ++ { ++ /* replace an existing function, without any label */ ++ required = SEPG_DB_PROCEDURE__SETATTR; ++ sid = sepgsqlGetTupleSecid(ProcedureRelationId, oldTup, NULL); ++ } ++ else ++ { ++ /* replace an existing function, with relabeling */ ++ sepgsql_proc_common(HeapTupleGetOid(oldTup), ++ SEPG_DB_PROCEDURE__SETATTR | ++ SEPG_DB_PROCEDURE__RELABELFROM, true); ++ ++ required = SEPG_DB_PROCEDURE__RELABELTO; ++ sid = sepgsqlGetTupleSecid(ProcedureRelationId, oldTup, NULL); ++ } ++ ++ /* Procedural language is trusted? */ ++ tuple = SearchSysCache(LANGOID, ++ ObjectIdGetDatum(langOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for procedural langugage: %u", langOid); ++ ++ trusted = ((Form_pg_language) GETSTRUCT(tuple))->lanpltrusted; ++ if (!trusted) ++ required |= SEPG_DB_PROCEDURE__UNTRUSTED; ++ ++ ReleaseSysCache(tuple); ++ ++ /* check it */ ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_PROCEDURE, ++ required, procName, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_proc_alter(Oid procOid, const char *newName, Oid newNsp) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ if (newName || OidIsValid(newNsp)) ++ { ++ HeapTuple tuple; ++ Oid oldNsp; ++ ++ tuple = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(procOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for function %u", procOid); ++ ++ oldNsp = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; ++ ++ ReleaseSysCache(tuple); ++ ++ if (!OidIsValid(newNsp)) ++ { ++ sepgsql_schema_common(oldNsp, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ else ++ { ++ sepgsql_schema_common(oldNsp, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); ++ } ++ } ++ } ++ ++ void ++ sepgsql_proc_drop(Oid procOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__DROP, true); ++ } ++ ++ void ++ sepgsql_proc_grant(Oid procOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ } ++ ++ Oid ++ sepgsql_proc_relabel(Oid procOid, DefElem *newLabel) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ { ++ if (newLabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux is disabled now"))); ++ return InvalidOid; ++ } ++ ++ sid.relid = ProcedureRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ ++ /* db_procedure:{setattr relabelfrom} for older seclabel */ ++ sepgsql_proc_common(procOid, ++ SEPG_DB_PROCEDURE__SETATTR | ++ SEPG_DB_PROCEDURE__RELABELFROM, true); ++ /* db_procedure:{relabelto} for newer seclabel */ ++ sepgsqlClientHasPerms(sid, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_DB_PROCEDURE__RELABELTO, ++ get_func_name(procOid), true); ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_proc_execute(Oid procOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__EXECUTE, true); ++ } ++ ++ bool ++ sepgsql_proc_hint_inlined(HeapTuple protup) ++ { ++ security_context_t newcon; ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return true; ++ ++ if (!sepgsql_proc_common(HeapTupleGetOid(protup), ++ SEPG_DB_PROCEDURE__EXECUTE, false)) ++ return false; ++ /* ++ * If the security context of client is unchange ++ * before or after invocation of the functions, ++ * it is not a trusted procedure, so it can be ++ * inlined due to performance purpose. ++ */ ++ sid = sepgsqlGetTupleSecid(ProcedureRelationId, protup, NULL); ++ ++ newcon = sepgsqlClientCreateLabel(sid, SEPG_CLASS_PROCESS); ++ ++ if (strcmp(sepgsqlGetClientLabel(), newcon) == 0) ++ return true; ++ ++ return false; ++ } ++ ++ /* ++ * sepgsqlCheckProcedureEntrypoint ++ * checks whether the given function call causes domain transition, ++ * or not. If it needs a domain transition, it injects a wrapper ++ * function to invoke it under new domain. ++ */ ++ struct TrustedProcedureCache ++ { ++ FmgrInfo flinfo; ++ char newcon[1]; ++ }; ++ ++ static Datum ++ sepgsqlTrustedProcedure(PG_FUNCTION_ARGS) ++ { ++ struct TrustedProcedureCache *tcache; ++ security_context_t save_context; ++ FmgrInfo *save_flinfo; ++ Datum result; ++ ++ tcache = fcinfo->flinfo->fn_extra; ++ Assert(tcache != NULL); ++ ++ save_context = sepgsqlSwitchClient(tcache->newcon); ++ save_flinfo = fcinfo->flinfo; ++ fcinfo->flinfo = &tcache->flinfo; ++ ++ PG_TRY(); ++ { ++ result = FunctionCallInvoke(fcinfo); ++ } ++ PG_CATCH(); ++ { ++ sepgsqlSwitchClient(save_context); ++ fcinfo->flinfo = save_flinfo; ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ sepgsqlSwitchClient(save_context); ++ fcinfo->flinfo = save_flinfo; ++ ++ return result; ++ } ++ ++ void ++ sepgsql_proc_entrypoint(FmgrInfo *flinfo, HeapTuple protup) ++ { ++ struct TrustedProcedureCache *tcache; ++ security_context_t newcon; ++ sepgsql_sid_t proSid; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ proSid = sepgsqlGetTupleSecid(ProcedureRelationId, ++ protup, NULL); ++ ++ newcon = sepgsqlClientCreateLabel(proSid, SEPG_CLASS_PROCESS); ++ ++ /* Do nothing, if it is not a trusted procedure */ ++ if (strcmp(newcon, sepgsqlGetClientLabel()) == 0) ++ return; ++ ++ /* check db_procedure:{entrypoint} */ ++ sepgsqlClientHasPerms(proSid, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_DB_PROCEDURE__ENTRYPOINT, ++ NULL, true); ++ ++ /* check process:{transition} */ ++ sepgsqlComputePerms(sepgsqlGetClientLabel(), ++ newcon, ++ SEPG_CLASS_PROCESS, ++ SEPG_PROCESS__TRANSITION, ++ NULL, true); ++ ++ /* setup trusted procedure */ ++ tcache = MemoryContextAllocZero(flinfo->fn_mcxt, ++ sizeof(*tcache) + strlen(newcon)); ++ memcpy(&tcache->flinfo, flinfo, sizeof(*flinfo)); ++ strcpy(tcache->newcon, newcon); ++ flinfo->fn_addr = sepgsqlTrustedProcedure; ++ flinfo->fn_extra = tcache; ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_cast related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ Oid ++ sepgsql_cast_create(Oid sourceTypOid, Oid targetTypOid, Oid funcOid) ++ { ++ sepgsql_sid_t sid; ++ char audit_buffer[2*NAMEDATALEN+10]; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(CastRelationId); ++ ++ snprintf(audit_buffer, sizeof(audit_buffer), "%s::%s", ++ format_type_be(sourceTypOid), format_type_be(targetTypOid)); ++ ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ audit_buffer, true); ++ ++ if (OidIsValid(funcOid)) ++ sepgsql_proc_common(funcOid, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_cast_drop(Oid castOid) ++ { ++ Form_pg_cast castForm; ++ Relation rel; ++ HeapTuple tuple; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ char audit_buffer[2*NAMEDATALEN+10]; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ rel = heap_open(CastRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey, ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(castOid)); ++ ++ scan = systable_beginscan(rel, CastOidIndexId, true, ++ SnapshotNow, 1, &skey); ++ tuple = systable_getnext(scan); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "could not find tuple for cast: %u", castOid); ++ ++ castForm = (Form_pg_cast) GETSTRUCT(tuple); ++ ++ snprintf(audit_buffer, sizeof(audit_buffer), "%s::%s", ++ format_type_be(castForm->castsource), ++ format_type_be(castForm->casttarget)); ++ ++ sid = sepgsqlGetTupleSecid(CastRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ audit_buffer, true); ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, AccessShareLock); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_conversion related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ Oid ++ sepgsql_conversion_create(const char *convName, Oid nspOid, Oid procOid) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(ConversionRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ convName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} */ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_conversion_alter(Oid convOid, const char *newName) ++ { ++ Form_pg_conversion convForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(CONVOID, ++ ObjectIdGetDatum(convOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for conversion %u", convOid); ++ convForm = (Form_pg_conversion) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(ConversionRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(convForm->conname), true); ++ if (newName) ++ { ++ Oid nspOid = convForm->connamespace; ++ ++ sepgsql_schema_common(nspOid, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++ } ++ ++ void ++ sepgsql_conversion_drop(Oid convOid) ++ { ++ Form_pg_conversion convForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(CONVOID, ++ ObjectIdGetDatum(convOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for conversion %u", convOid); ++ convForm = (Form_pg_conversion) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(ConversionRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(convForm->conname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(convForm->connamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_foreign_data_wrapper related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ static bool ++ sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort) ++ { ++ Form_pg_foreign_data_wrapper fdwForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ bool rc; ++ ++ tuple = SearchSysCache(FOREIGNDATAWRAPPEROID, ++ ObjectIdGetDatum(fdwOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for FDW: %u", fdwOid); ++ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(ForeignDataWrapperRelationId, tuple, &tclass); ++ rc = sepgsqlClientHasPerms(sid, tclass, required, ++ NameStr(fdwForm->fdwname), abort); ++ ReleaseSysCache(tuple); ++ ++ return rc; ++ } ++ ++ Oid ++ sepgsql_fdw_create(const char *fdwName, Oid fdwValidator) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(ForeignDataWrapperRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ fdwName, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(fdwValidator)) ++ sepgsql_proc_common(fdwValidator, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_fdw_alter(Oid fdwOid, Oid newValidator) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(newValidator)) ++ sepgsql_proc_common(newValidator, SEPG_DB_PROCEDURE__INSTALL, true); ++ } ++ ++ void ++ sepgsql_fdw_drop(Oid fdwOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++ ++ void ++ sepgsql_fdw_grant(Oid fdwOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_foreign_server related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ static bool ++ sepgsql_foreign_server_common(Oid fsrvOid, uint32 required, bool abort) ++ { ++ Form_pg_foreign_server fsrvForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ bool rc; ++ ++ tuple = SearchSysCache(FOREIGNSERVEROID, ++ ObjectIdGetDatum(fsrvOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for foreign server %u", fsrvOid); ++ fsrvForm = (Form_pg_foreign_server) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(ForeignServerRelationId, tuple, &tclass); ++ rc = sepgsqlClientHasPerms(sid, tclass, required, ++ NameStr(fsrvForm->srvname), abort); ++ ReleaseSysCache(tuple); ++ ++ return rc; ++ } ++ ++ Oid ++ sepgsql_foreign_server_create(const char *fsrvName) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(ForeignServerRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ fsrvName, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_foreign_server_alter(Oid fsrvOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++ ++ void ++ sepgsql_foreign_server_drop(Oid fsrvOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++ ++ void ++ sepgsql_foreign_server_grant(Oid fsrvOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_language related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ static bool ++ sepgsql_language_common(Oid langOid, uint32 required, bool abort) ++ { ++ Form_pg_language langForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ bool rc; ++ ++ tuple = SearchSysCache(LANGOID, ++ ObjectIdGetDatum(langOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for language %u", langOid); ++ langForm = (Form_pg_language) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(LanguageRelationId, tuple, &tclass); ++ rc = sepgsqlClientHasPerms(sid, tclass, required, ++ NameStr(langForm->lanname), abort); ++ ++ ReleaseSysCache(tuple); ++ ++ return rc; ++ } ++ ++ Oid ++ sepgsql_language_create(const char *langName, Oid handlerOid, Oid validatorOid) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(LanguageRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, langName, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(handlerOid)) ++ sepgsql_proc_common(handlerOid, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(validatorOid)) ++ sepgsql_proc_common(validatorOid, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_language_alter(Oid langOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_language_common(langOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++ ++ void ++ sepgsql_language_drop(Oid langOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_language_common(langOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++ ++ void ++ sepgsql_language_grant(Oid langOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_language_common(langOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_largeobject related security hooks ++ * (need to backport v8.5 feature) ++ * ------------------------------------------------------------ */ ++ Oid ++ sepgsql_largeobject_create(Oid loid, DefElem *newLabel) ++ { ++ return InvalidOid; ++ } ++ ++ void ++ sepgsql_largeobject_alter(Oid loid, Oid newOwner) ++ {} ++ ++ void ++ sepgsql_largeobject_drop(Oid loid, bool dacSkip) ++ {} ++ ++ void ++ sepgsql_largeobject_read(Oid loid) ++ {} ++ ++ void ++ sepgsql_largeobject_write(Oid loid) ++ {} ++ ++ void ++ sepgsql_largeobject_export(Oid loid, const char *filename) ++ {} ++ ++ Oid ++ sepgsql_largeobject_import(Oid loid, const char *filename, DefElem *newLabel) ++ {} ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_opclass related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ Oid ++ sepgsql_opclass_create(const char *opcName, Oid nspOid) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(OperatorClassRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ opcName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_opclass_alter(Oid opcOid, const char *newName) ++ { ++ Form_pg_opclass opcForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(CLAOID, ++ ObjectIdGetDatum(opcOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for opclass %u", opcOid); ++ opcForm = (Form_pg_opclass) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(OperatorClassRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(opcForm->opcname), true); ++ ++ /* db_schema:{add_name remove_name} */ ++ if (newName) ++ { ++ sepgsql_schema_common(opcForm->opcnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++ } ++ ++ void ++ sepgsql_opclass_drop(Oid opcOid) ++ { ++ Form_pg_opclass opcForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(CLAOID, ++ ObjectIdGetDatum(opcOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for opclass %u", opcOid); ++ opcForm = (Form_pg_opclass) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(OperatorClassRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(opcForm->opcname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(opcForm->opcnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_opfamily related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ Oid ++ sepgsql_opfamily_create(const char *opfName, Oid nspOid) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(OperatorFamilyRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ opfName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_opfamily_alter(Oid opfOid, const char *newName) ++ { ++ Form_pg_opfamily opfForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(OPFAMILYOID, ++ ObjectIdGetDatum(opfOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for operator family: %u", opfOid); ++ opfForm = (Form_pg_opfamily) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(opfForm->opfname), true); ++ if (newName) ++ { ++ sepgsql_schema_common(opfForm->opfnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++ } ++ ++ void ++ sepgsql_opfamily_drop(Oid opfOid) ++ { ++ Form_pg_opfamily opfForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(OPFAMILYOID, ++ ObjectIdGetDatum(opfOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for operator family: %u", opfOid); ++ opfForm = (Form_pg_opfamily) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(opfForm->opfname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(opfForm->opfnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ void ++ sepgsql_opfamily_add_operator(Oid opfOid, Oid operOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* currently, do nothing here */ ++ } ++ ++ void ++ sepgsql_opfamily_add_procedure(Oid opfOid, Oid procOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* ++ * Note that db_tuple:{setattr} is already checked at the ++ * earlier phase, so db_procedure:{install} is only needed. ++ */ ++ if (OidIsValid(procOid)) ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_operator related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ static bool ++ sepgsql_operator_common(Oid oprOid, uint32 required, bool abort) ++ { ++ Form_pg_operator oprForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ bool rc; ++ ++ tuple = SearchSysCache(OPEROID, ++ ObjectIdGetDatum(oprOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for operator: %u", oprOid); ++ oprForm = (Form_pg_operator) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(OperatorRelationId, tuple, &tclass); ++ rc = sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(oprForm->oprname), abort); ++ ++ ReleaseSysCache(tuple); ++ ++ return rc; ++ } ++ ++ Oid ++ sepgsql_operator_create(const char *oprName, Oid oprOid, Oid nspOid, ++ Oid codeFn, Oid restFn, Oid joinFn) ++ { ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint32 required; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ if (!OidIsValid(oprOid)) ++ { ++ sid = sepgsqlGetDefaultTupleSecid(OperatorRelationId); ++ required = SEPG_DB_TUPLE__INSERT; ++ } ++ else ++ { ++ tuple = SearchSysCache(OPEROID, ++ ObjectIdGetDatum(oprOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for operator %u", oprOid); ++ ++ sid = sepgsqlGetTupleSecid(OperatorRelationId, tuple, NULL); ++ ++ ReleaseSysCache(tuple); ++ ++ required = SEPG_DB_TUPLE__UPDATE; ++ } ++ ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ required, oprName, true); ++ ++ /* db_schema:{add_name} checks */ ++ if (!OidIsValid(oprOid)) ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} checks */ ++ if (OidIsValid(codeFn)) ++ sepgsql_proc_common(codeFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(restFn)) ++ sepgsql_proc_common(restFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(joinFn)) ++ sepgsql_proc_common(joinFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_operator_alter(Oid oprOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_operator_common(oprOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++ ++ void ++ sepgsql_operator_drop(Oid oprOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_operator_common(oprOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_rewrite related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ void ++ sepgsql_rule_create(Oid relOid, const char *ruleName) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ } ++ ++ void ++ sepgsql_rule_drop(Oid relOid, const char *ruleName) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_trigger related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ void ++ sepgsql_trigger_create(Oid relOid, const char *trigName, Oid procOid) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* db_table:{setattr} */ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ ++ /* db_procedure:{install} */ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); ++ } ++ ++ void ++ sepgsql_trigger_alter(Oid relOid, const char *trigName) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* db_table:{setattr} */ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ } ++ ++ void ++ sepgsql_trigger_drop(Oid relOid, const char *trigName) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* db_table:{setattr} */ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_type related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ Oid ++ sepgsql_ts_config_create(const char *cfgName, Oid nspOid) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(TSConfigRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ cfgName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_ts_config_alter(Oid cfgOid, const char *newName) ++ { ++ Form_pg_ts_config cfgForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSCONFIGOID, ++ ObjectIdGetDatum(cfgOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search dictionary %u", cfgOid); ++ cfgForm = (Form_pg_ts_config) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSConfigRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(cfgForm->cfgname), true); ++ if (newName) ++ { ++ sepgsql_schema_common(cfgForm->cfgnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++ } ++ ++ void ++ sepgsql_ts_config_drop(Oid cfgOid) ++ { ++ Form_pg_ts_config cfgForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSCONFIGOID, ++ ObjectIdGetDatum(cfgOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search dictionary %u", cfgOid); ++ cfgForm = (Form_pg_ts_config) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSConfigRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(cfgForm->cfgname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(cfgForm->cfgnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_type related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ Oid ++ sepgsql_ts_dict_create(const char *dictName, Oid nspOid) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(TSDictionaryRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ dictName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_ts_dict_alter(Oid dictOid, const char *newName) ++ { ++ Form_pg_ts_dict dictForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSDICTOID, ++ ObjectIdGetDatum(dictOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); ++ dictForm = (Form_pg_ts_dict) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSDictionaryRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(dictForm->dictname), true); ++ ++ /* db_schema:{add_name remove_name} */ ++ if (newName) ++ { ++ sepgsql_schema_common(dictForm->dictnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++ } ++ ++ void ++ sepgsql_ts_dict_drop(Oid dictOid) ++ { ++ Form_pg_ts_dict dictForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSDICTOID, ++ ObjectIdGetDatum(dictOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); ++ dictForm = (Form_pg_ts_dict) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSDictionaryRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(dictForm->dictname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(dictForm->dictnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_type related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ Oid ++ sepgsql_ts_parser_create(const char *prsName, Oid nspOid, ++ Oid startFn, Oid tokenFn, Oid sendFn, ++ Oid headlineFn, Oid lextypeFn) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(TSParserRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ prsName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(startFn)) ++ sepgsql_proc_common(startFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(tokenFn)) ++ sepgsql_proc_common(tokenFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(sendFn)) ++ sepgsql_proc_common(sendFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(headlineFn)) ++ sepgsql_proc_common(headlineFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(lextypeFn)) ++ sepgsql_proc_common(lextypeFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_ts_parser_alter(Oid prsOid, const char *newName) ++ { ++ Form_pg_ts_parser prsForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSPARSEROID, ++ ObjectIdGetDatum(prsOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search parser %u", prsOid); ++ ++ prsForm = (Form_pg_ts_parser) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSParserRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(prsForm->prsname), true); ++ if (newName) ++ { ++ sepgsql_schema_common(prsForm->prsnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++ } ++ ++ void ++ sepgsql_ts_parser_drop(Oid prsOid) ++ { ++ Form_pg_ts_parser prsForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSPARSEROID, ++ ObjectIdGetDatum(prsOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search parser %u", prsOid); ++ ++ prsForm = (Form_pg_ts_parser) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSParserRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(prsForm->prsname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(prsForm->prsnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_type related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ Oid ++ sepgsql_ts_template_create(const char *tmplName, Oid nspOid, ++ Oid initFn, Oid lexizeFn) ++ { ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(TSTemplateRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ tmplName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(initFn)) ++ sepgsql_proc_common(initFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(lexizeFn)) ++ sepgsql_proc_common(lexizeFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_ts_template_alter(Oid tmplOid, const char *newName) ++ { ++ Form_pg_ts_template tmplForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSTEMPLATEOID, ++ ObjectIdGetDatum(tmplOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search template %u", tmplOid); ++ tmplForm = (Form_pg_ts_template) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSTemplateRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(tmplForm->tmplname), true); ++ if (newName) ++ { ++ sepgsql_schema_common(tmplForm->tmplnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++ } ++ ++ void ++ sepgsql_ts_template_drop(Oid tmplOid) ++ { ++ Form_pg_ts_template tmplForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSTEMPLATEOID, ++ ObjectIdGetDatum(tmplOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search template %u", tmplOid); ++ tmplForm = (Form_pg_ts_template) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSTemplateRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(tmplForm->tmplname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(tmplForm->tmplnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Pg_type related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ Oid ++ sepgsql_type_create(const char *typName, HeapTuple oldTup, Oid nspOid, ++ Oid inputProc, Oid outputProc, Oid recvProc, Oid sendProc, ++ Oid modinProc, Oid modoutProc, Oid analyzeProc) ++ { ++ sepgsql_sid_t sid; ++ uint32 required; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ if (!HeapTupleIsValid(oldTup)) ++ { ++ sid = sepgsqlGetDefaultTupleSecid(TypeRelationId); ++ required = SEPG_DB_TUPLE__INSERT; ++ } ++ else ++ { ++ sid = sepgsqlGetTupleSecid(TypeRelationId, oldTup, NULL); ++ required = SEPG_DB_TUPLE__UPDATE; ++ } ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ required, typName, true); ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(inputProc)) ++ sepgsql_proc_common(inputProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(outputProc)) ++ sepgsql_proc_common(outputProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(recvProc)) ++ sepgsql_proc_common(recvProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(sendProc)) ++ sepgsql_proc_common(sendProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(modinProc)) ++ sepgsql_proc_common(modinProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(modoutProc)) ++ sepgsql_proc_common(modoutProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(analyzeProc)) ++ sepgsql_proc_common(analyzeProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++ } ++ ++ void ++ sepgsql_type_alter(Oid typOid, const char *newName, Oid newNsp) ++ { ++ Form_pg_type typForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TYPEOID, ++ ObjectIdGetDatum(typOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for type: %u", typOid); ++ typForm = (Form_pg_type) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TypeRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(typForm->typname), true); ++ ++ if (newName || OidIsValid(newNsp)) ++ { ++ Oid oldNsp = typForm->typnamespace; ++ ++ if (!OidIsValid(newNsp)) ++ { ++ sepgsql_schema_common(oldNsp, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ else ++ { ++ sepgsql_schema_common(oldNsp, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); ++ } ++ } ++ ReleaseSysCache(tuple); ++ } ++ ++ void ++ sepgsql_type_drop(Oid typOid) ++ { ++ Form_pg_type typForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TYPEOID, ++ ObjectIdGetDatum(typOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for type: %u", typOid); ++ typForm = (Form_pg_type) GETSTRUCT(tuple); ++ ++ if (typForm->typtype == TYPTYPE_COMPOSITE || ++ (typForm->typtype == TYPTYPE_BASE && OidIsValid(typForm->typarray))) ++ { ++ /* ++ * No need to check for composite type and implicitly ++ * declared array type here. ++ */ ++ ReleaseSysCache(tuple); ++ return; ++ } ++ ++ sid = sepgsqlGetTupleSecid(TypeRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(typForm->typname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(typForm->typnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Misc system object related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ ++ void ++ sepgsql_sysobj_drop(const ObjectAddress *object) ++ { ++ switch (object->classId) ++ { ++ case RelationRelationId: ++ if (object->objectSubId == 0) ++ sepgsql_relation_drop(object->objectId); ++ else ++ sepgsql_attribute_drop(object->objectId, ++ object->objectSubId); ++ break; ++ ++ case ProcedureRelationId: ++ sepgsql_proc_drop(object->objectId); ++ break; ++ ++ case TypeRelationId: ++ sepgsql_type_drop(object->objectId); ++ break; ++ ++ case CastRelationId: ++ sepgsql_cast_drop(object->objectId); ++ break; ++ ++ case ConversionRelationId: ++ sepgsql_conversion_drop(object->objectId); ++ break; ++ ++ case LanguageRelationId: ++ sepgsql_language_drop(object->objectId); ++ break; ++ ++ case OperatorRelationId: ++ sepgsql_operator_drop(object->objectId); ++ break; ++ ++ case OperatorClassRelationId: ++ sepgsql_opclass_drop(object->objectId); ++ break; ++ ++ case OperatorFamilyRelationId: ++ sepgsql_opfamily_drop(object->objectId); ++ break; ++ ++ case NamespaceRelationId: ++ sepgsql_schema_drop(object->objectId); ++ break; ++ ++ case TSParserRelationId: ++ sepgsql_ts_parser_drop(object->objectId); ++ break; ++ ++ case TSDictionaryRelationId: ++ sepgsql_ts_dict_drop(object->objectId); ++ break; ++ ++ case TSTemplateRelationId: ++ sepgsql_ts_template_drop(object->objectId); ++ break; ++ ++ case TSConfigRelationId: ++ sepgsql_ts_config_drop(object->objectId); ++ break; ++ ++ case AuthIdRelationId: ++ break; ++ ++ case DatabaseRelationId: ++ sepgsql_database_drop(object->objectId); ++ break; ++ ++ case TableSpaceRelationId: ++ break; ++ ++ case ForeignDataWrapperRelationId: ++ sepgsql_fdw_drop(object->objectId); ++ break; ++ ++ case ForeignServerRelationId: ++ sepgsql_foreign_server_drop(object->objectId); ++ break; ++ ++ case UserMappingRelationId: ++ break; ++ ++ default: ++ /* do nothing */ ++ break; ++ } ++ } ++ ++ /* ------------------------------------------------------------ * ++ * ++ * Filesystem object related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ static char * ++ sepgsql_getfilecon(const char *path) ++ { ++ security_context_t context; ++ char *result; ++ ++ if (getfilecon_raw(path, &context) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not get context of \"%s\": %m", path))); ++ ++ PG_TRY(); ++ { ++ result = pstrdup(context); ++ } ++ PG_CATCH(); ++ { ++ freecon(context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(context); ++ ++ return result; ++ } ++ ++ static void ++ sepgsql_file_common(const char *filename, uint32 required, bool may_create) ++ { ++ struct stat stbuf; ++ ++ if (stat(filename, &stbuf) == 0) ++ { ++ uint16 tclass; ++ ++ /* ++ * Get file object class ++ */ ++ if (S_ISDIR(stbuf.st_mode)) ++ tclass = SEPG_CLASS_DIR; ++ else if (S_ISCHR(stbuf.st_mode)) ++ tclass = SEPG_CLASS_CHR_FILE; ++ else if (S_ISBLK(stbuf.st_mode)) ++ tclass = SEPG_CLASS_BLK_FILE; ++ else if (S_ISFIFO(stbuf.st_mode)) ++ tclass = SEPG_CLASS_FIFO_FILE; ++ else if (S_ISLNK(stbuf.st_mode)) ++ tclass = SEPG_CLASS_LNK_FILE; ++ else if (S_ISSOCK(stbuf.st_mode)) ++ tclass = SEPG_CLASS_SOCK_FILE; ++ else ++ tclass = SEPG_CLASS_FILE; ++ ++ /* ++ * Check permission (no cached operation) ++ */ ++ sepgsqlComputePerms(sepgsqlGetClientLabel(), ++ sepgsql_getfilecon(filename), ++ tclass, required, ++ filename, true); ++ } ++ else if (may_create) ++ { ++ /* ++ * If the required file is not found, we check permission to ++ * create a new file and required permission on the new file. ++ */ ++ security_context_t dcontext; ++ security_context_t ncontext; ++ char *copy = pstrdup(filename); ++ ++ /* ++ * Compute a security context for the new file ++ */ ++ dcontext = sepgsql_getfilecon(dirname(copy)); ++ ++ ncontext = sepgsqlComputeCreate(sepgsqlGetServerLabel(), ++ dcontext, ++ SEPG_CLASS_FILE); ++ /* ++ * Check permission (no cached operation) ++ */ ++ required |= SEPG_FILE__CREATE; ++ ++ sepgsqlComputePerms(sepgsqlGetClientLabel(), ++ sepgsql_getfilecon(filename), ++ SEPG_CLASS_FILE, ++ required, filename, true); ++ } ++ else ++ { ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not stat file \"%s\": %m", filename))); ++ } ++ } ++ ++ void ++ sepgsql_file_stat(const char *filename) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_file_common(filename, SEPG_FILE__GETATTR, false); ++ } ++ ++ void ++ sepgsql_file_read(const char *filename) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_file_common(filename, SEPG_FILE__READ, false); ++ } ++ ++ void ++ sepgsql_file_write(const char *filename) ++ { ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_file_common(filename, SEPG_FILE__WRITE, true); ++ } ++ ++ /* ++ * TODO: add check for pg_ls_dir() ++ */ +diff -Nrpc base/src/backend/security/sepgsql/checker.c sepgsql/src/backend/security/sepgsql/checker.c +*** base/src/backend/security/sepgsql/checker.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/checker.c Thu Oct 8 09:29:32 2009 +*************** +*** 0 **** +--- 1,424 ---- + /* + * src/backend/security/sepgsql/checker.c + * walks on given Query tree and applies checks @@ -8170,7 +12296,7 @@ diff -Nrpc base/src/backend/security/sepgsql/checker.c sepgsql/src/backend/secur + + relForm = (Form_pg_class) GETSTRUCT(reltup); + -+ relsid = sepgsqlGetTupleContext(RelationRelationId, reltup, &tclass); ++ relsid = sepgsqlGetTupleSecid(RelationRelationId, reltup, &tclass); + + if (tclass != SEPG_CLASS_DB_TABLE) + { @@ -8234,8 +12360,8 @@ diff -Nrpc base/src/backend/security/sepgsql/checker.c sepgsql/src/backend/secur + snprintf(auname, sizeof(auname), "%s.%s", + NameStr(relForm->relname), + NameStr(attForm->attname)); -+ attsid = sepgsqlGetTupleContext(AttributeRelationId, -+ atttup, &tclass); ++ attsid = sepgsqlGetTupleSecid(AttributeRelationId, ++ atttup, &tclass); + sepgsqlClientHasPerms(attsid, tclass, attperms, auname, true); + + ReleaseSysCache(atttup); @@ -8334,25 +12460,6 @@ diff -Nrpc base/src/backend/security/sepgsql/checker.c sepgsql/src/backend/secur + } + + /* -+ * sepgsqlCheckSelectInto -+ * It checks db_table/db_column:{insert} on the table newly created -+ */ -+ void -+ sepgsqlCheckSelectInto(Oid relationId) -+ { -+ Bitmapset *modified = NULL; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ modified = bms_add_member(modified, InvalidAttrNumber -+ - FirstLowInvalidHeapAttributeNumber); -+ -+ checkTabelColumnPerms(relationId, NULL, modified, -+ SEPG_DB_TABLE__INSERT); -+ } -+ -+ /* + * sepgsqlExecScan + * makes a decision on the given tuple. + */ @@ -8368,7 +12475,7 @@ diff -Nrpc base/src/backend/security/sepgsql/checker.c sepgsql/src/backend/secur + RelationGetRelid(rel) == SecurityRelationId) + return true; + -+ sid = sepgsqlGetTupleContext(RelationGetRelid(rel), tuple, &tclass); ++ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass); + /* + * Insert/Delete to an external attribute is equivalent to + * the set-attribute on the master @@ -8436,8 +12543,8 @@ diff -Nrpc base/src/backend/security/sepgsql/checker.c sepgsql/src/backend/secur + if (internal) + return; + -+ sid = sepgsqlGetTupleContext(RelationGetRelid(rel), -+ newtup, &tclass); ++ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), ++ newtup, &tclass); + sepgsqlClientHasPerms(sid, tclass, SEPG_DB_TUPLE__INSERT, NULL, true); + } + @@ -8482,15 +12589,15 @@ diff -Nrpc base/src/backend/security/sepgsql/checker.c sepgsql/src/backend/secur + uint16 tclass; + + /* db_tuple:{relabelfrom} for older security context */ -+ sid = sepgsqlGetTupleContext(RelationGetRelid(rel), -+ &oldtup, &tclass); ++ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), ++ &oldtup, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__RELABELFROM, + NULL, true); + + /* db_tuple:{relabelto} for newer security label */ -+ sid = sepgsqlGetTupleContext(RelationGetRelid(rel), -+ newtup, &tclass); ++ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), ++ newtup, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__RELABELTO, + NULL, true); @@ -8584,10 +12691,10 @@ diff -Nrpc base/src/backend/security/sepgsql/dummy.c sepgsql/src/backend/securit + } diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/security/sepgsql/hooks.c *** base/src/backend/security/sepgsql/hooks.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/hooks.c Thu Sep 10 00:06:47 2009 +--- sepgsql/src/backend/security/sepgsql/hooks.c Mon Sep 28 10:07:19 2009 *************** *** 0 **** ---- 1,1273 ---- +--- 1,239 ---- + /* + * src/backend/security/sepgsql/hooks.c + * SE-PostgreSQL security hooks @@ -8624,897 +12731,11 @@ diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/securit + #include "utils/syscache.h" + #include "utils/tqual.h" + -+ /* -+ * ------------------------------------------------------------ -+ * Hooks corresponding to db_database object class -+ * ------------------------------------------------------------ -+ * -+ * sepgsqlCheckDatabaseInstallModule -+ * checks db_database:{install_module} permission when the client -+ * tries to install a dynamic link library on the current databse. -+ * -+ * sepgsqlCheckDatabaseLoadModule -+ * checks capability of the database when it loads a certain DLL -+ * into its process address space. -+ */ -+ Oid -+ sepgsqlCheckDatabaseCreate(const char *datname, DefElem *newLabel) -+ { -+ sepgsql_sid_t datSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!newLabel) -+ datSid = sepgsqlGetDefaultDatabaseSecid(); -+ else -+ { -+ datSid.relid = DatabaseRelationId; -+ datSid.secid = securityTransSecLabelIn(datSid.relid, -+ strVal(newLabel->arg)); -+ } -+ -+ sepgsqlClientHasPerms(datSid, -+ SEPG_CLASS_DB_DATABASE, -+ SEPG_DB_DATABASE__CREATE, -+ datname, true); -+ return datSid.secid; -+ } -+ -+ static bool -+ checkDatabaseCommon(Oid datOid, uint32 required, bool abort) -+ { -+ HeapTuple tuple; -+ sepgsql_sid_t datSid; -+ uint16 tclass; -+ const char *auname; -+ bool rc; -+ -+ if (!sepgsqlIsEnabled()) -+ return true; -+ -+ 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); -+ datSid = sepgsqlGetTupleContext(DatabaseRelationId, -+ tuple, &tclass); -+ rc = sepgsqlClientHasPerms(datSid, -+ tclass, required, -+ auname, abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+ } -+ -+ void -+ sepgsqlCheckDatabaseDrop(Oid datOid) -+ { -+ checkDatabaseCommon(datOid, SEPG_DB_DATABASE__DROP, true); -+ } -+ -+ void -+ sepgsqlCheckDatabaseSetattr(Oid datOid) -+ { -+ checkDatabaseCommon(datOid, SEPG_DB_DATABASE__SETATTR, true); -+ } -+ -+ Oid -+ sepgsqlCheckDatabaseRelabel(Oid datOid, DefElem *newLabel) -+ { -+ sepgsql_sid_t datSid; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ datSid.relid = DatabaseRelationId; -+ datSid.secid = securityTransSecLabelIn(datSid.relid, -+ strVal(newLabel->arg)); -+ /* db_database:{setattr relabelfrom} for older seclabel */ -+ checkDatabaseCommon(datOid, -+ SEPG_DB_DATABASE__SETATTR | -+ SEPG_DB_DATABASE__RELABELFROM, true); -+ /* db_database:{relabelto} for newer seclabel */ -+ sepgsqlClientHasPerms(datSid, -+ SEPG_CLASS_DB_DATABASE, -+ SEPG_DB_DATABASE__RELABELTO, -+ get_database_name(datOid), true); -+ return datSid.secid; -+ } -+ -+ void -+ sepgsqlCheckDatabaseAccess(Oid datOid) -+ { -+ if (!checkDatabaseCommon(datOid, SEPG_DB_DATABASE__ACCESS, false)) -+ ereport(FATAL, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: security policy violation"))); -+ } -+ -+ bool -+ sepgsqlCheckDatabaseSuperuser(void) -+ { -+ return checkDatabaseCommon(MyDatabaseId, -+ SEPG_DB_DATABASE__SUPERUSER, false); -+ } -+ -+ void -+ sepgsqlCheckDatabaseLoadModule(const char *filename) -+ { -+ HeapTuple tuple; -+ security_context_t filecon; -+ security_context_t dbcon; -+ -+ 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(MyDatabaseId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for database: %u", MyDatabaseId); -+ -+ dbcon = 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(dbcon, -+ filecon, -+ SEPG_CLASS_DB_DATABASE, -+ SEPG_DB_DATABASE__LOAD_MODULE, -+ filename, true); -+ } -+ PG_CATCH(); -+ { -+ freecon(filecon); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(filecon); -+ } -+ -+ /* -+ * ------------------------------------------------------------ -+ * Hooks corresponding to db_schema object class -+ * ------------------------------------------------------------ -+ */ -+ -+ Oid -+ sepgsqlCheckSchemaCreate(const char *nspName, DefElem *newLabel, bool isTemp) -+ { -+ sepgsql_sid_t nspSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!newLabel) -+ { -+ nspSid = (!isTemp -+ ? sepgsqlGetDefaultSchemaSecid(MyDatabaseId) -+ : sepgsqlGetDefaultSchemaTempSecid(MyDatabaseId)); -+ } -+ else -+ { -+ nspSid.relid = NamespaceRelationId; -+ nspSid.secid = securityTransSecLabelIn(nspSid.relid, -+ strVal(newLabel->arg)); -+ } -+ sepgsqlClientHasPerms(nspSid, -+ (!isTemp -+ ? SEPG_CLASS_DB_SCHEMA -+ : SEPG_CLASS_DB_SCHEMA_TEMP), -+ SEPG_DB_SCHEMA__CREATE, -+ nspName, true); -+ return nspSid.secid; -+ } -+ -+ static bool -+ checkSchemaCommon(Oid nspOid, uint32 required, bool abort) -+ { -+ HeapTuple tuple; -+ sepgsql_sid_t nspSid; -+ uint16 tclass; -+ const char *auname; -+ bool rc; -+ -+ if (!sepgsqlIsEnabled()) -+ return true; -+ -+ tuple = SearchSysCache(NAMESPACEOID, -+ ObjectIdGetDatum(nspOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for namespace: %u", nspOid); -+ -+ nspSid = sepgsqlGetTupleContext(NamespaceRelationId, -+ tuple, &tclass); -+ -+ auname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); -+ rc = sepgsqlClientHasPerms(nspSid, tclass, required, -+ auname, abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+ } -+ -+ void -+ sepgsqlCheckSchemaDrop(Oid nspOid) -+ { -+ checkSchemaCommon(nspOid, SEPG_DB_SCHEMA__DROP, true); -+ } -+ -+ void -+ sepgsqlCheckSchemaSetattr(Oid nspOid) -+ { -+ checkSchemaCommon(nspOid, SEPG_DB_SCHEMA__SETATTR, true); -+ } -+ -+ Oid -+ sepgsqlCheckSchemaRelabel(Oid nspOid, DefElem *newLabel) -+ { -+ sepgsql_sid_t nspSid; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ nspSid.relid = NamespaceRelationId; -+ nspSid.secid = securityTransSecLabelIn(nspSid.relid, -+ strVal(newLabel->arg)); -+ -+ /* db_schema:{setattr relabelfrom} for older seclabel */ -+ checkSchemaCommon(nspOid, -+ SEPG_DB_SCHEMA__SETATTR | -+ SEPG_DB_SCHEMA__RELABELFROM, true); -+ /* db_schema:{relabelto} for newer seclabel */ -+ sepgsqlClientHasPerms(nspSid, -+ !isAnyTempNamespace(nspOid) -+ ? SEPG_CLASS_DB_SCHEMA -+ : SEPG_CLASS_DB_SCHEMA_TEMP, -+ SEPG_DB_SCHEMA__RELABELTO, -+ get_namespace_name(nspOid), true); -+ return nspSid.secid; -+ } -+ -+ void -+ sepgsqlCheckSchemaAddName(Oid nspOid) -+ { -+ checkSchemaCommon(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ } -+ -+ void -+ sepgsqlCheckSchemaRemoveName(Oid nspOid) -+ { -+ checkSchemaCommon(nspOid, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ -+ bool -+ sepgsqlCheckSchemaSearch(Oid nspOid, bool abort) -+ { -+ return checkSchemaCommon(nspOid, SEPG_DB_SCHEMA__SEARCH, abort); -+ } -+ + /* ------------------------------------------------------------ * -+ * Hooks corresponding to db_column object class ++ * Hooks corresponding to db_blob object class + * ------------------------------------------------------------ */ + + /* -+ * NOTE: db_column:{create} is checked on sepgsqlCreateTableColumns() -+ * which is invoked on CREATE TABLE statement. -+ * The sepgsqlCheckColumnCreate() is called on the ALTER TABLE -+ * ... ADD COLUMN path. -+ */ -+ Oid -+ sepgsqlCheckColumnCreate(Oid table_oid, const char *attname, DefElem *newLabel) -+ { -+ sepgsql_sid_t attSid; -+ char relkind; -+ char auname[NAMEDATALEN * 2 + 3]; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ -+ relkind = get_rel_relkind(table_oid); -+ if (relkind != RELKIND_RELATION) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("Unable to assign security label"))); -+ return InvalidOid; -+ } -+ -+ if (!newLabel) -+ attSid = sepgsqlGetDefaultColumnSecid(table_oid); -+ else -+ { -+ attSid.relid = AttributeRelationId; -+ attSid.secid = securityTransSecLabelIn(attSid.relid, -+ strVal(newLabel->arg)); -+ } -+ -+ sprintf(auname, "%s.%s", get_rel_name(table_oid), attname); -+ sepgsqlClientHasPerms(attSid, -+ SEPG_CLASS_DB_COLUMN, -+ SEPG_DB_COLUMN__CREATE, -+ auname, true); -+ return attSid.secid; -+ } -+ -+ static void -+ checkColumnCommon(Oid relOid, AttrNumber attno, uint32 required) -+ { -+ Form_pg_attribute attr; -+ sepgsql_sid_t attSid; -+ HeapTuple tuple; -+ uint16 tclass; -+ char auname[2 * NAMEDATALEN + 3]; -+ char relkind; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ relkind = get_rel_relkind(relOid); -+ if (relkind != RELKIND_RELATION) -+ return; -+ -+ tuple = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(relOid), -+ Int16GetDatum(attno), -+ 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for attribute %d of relation %u", -+ attno, relOid); -+ -+ attr = (Form_pg_attribute) GETSTRUCT(tuple); -+ if (!attr->attisdropped) -+ { -+ sprintf(auname, "%s.%s", -+ get_rel_name(relOid), -+ NameStr(attr->attname)); -+ attSid = sepgsqlGetTupleContext(AttributeRelationId, -+ tuple, &tclass); -+ sepgsqlClientHasPerms(attSid, tclass, required, -+ auname, true); -+ } -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ void -+ sepgsqlCheckColumnDrop(Oid relOid, AttrNumber attno) -+ { -+ checkColumnCommon(relOid, attno, SEPG_DB_COLUMN__DROP); -+ } -+ -+ void -+ sepgsqlCheckColumnSetattr(Oid relOid, AttrNumber attno) -+ { -+ checkColumnCommon(relOid, attno, SEPG_DB_COLUMN__SETATTR); -+ } -+ -+ Oid -+ sepgsqlCheckColumnRelabel(Oid relOid, AttrNumber attno, DefElem *newLabel) -+ { -+ sepgsql_sid_t attSid; -+ char relkind; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ -+ relkind = get_rel_relkind(relOid); -+ if (relkind != RELKIND_RELATION) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s.%s\"", -+ get_rel_name(relOid), -+ get_attname(relOid, attno)))); -+ -+ attSid.relid = AttributeRelationId; -+ attSid.secid = securityTransSecLabelIn(attSid.relid, -+ strVal(newLabel->arg)); -+ -+ /* db_column:{setattr relabelfrom} for older seclabel */ -+ checkColumnCommon(relOid, attno, -+ SEPG_DB_COLUMN__SETATTR | -+ SEPG_DB_COLUMN__RELABELFROM); -+ -+ /* db_column:{relabelto} for newer seclabel */ -+ sepgsqlClientHasPerms(attSid, -+ SEPG_CLASS_DB_COLUMN, -+ SEPG_DB_COLUMN__RELABELTO, -+ get_attname(relOid, attno), true); -+ return attSid.secid; -+ } -+ -+ /* ------------------------------------------------------------ * -+ * Hooks corresponding to db_table object class -+ * ------------------------------------------------------------ */ -+ -+ /* -+ * NOTE: db_table/db_sequence:{create} permission is checked -+ * at sepgsqlCreateTableColumns() due to the reason -+ * for implementation. -+ * -+ * sepgsqlCheckTableReference -+ * checks db_table:{reference} and db_column:{reference} permission -+ * when the client tries to set up a foreign key constraint on the -+ * certain tables and columns. -+ */ -+ -+ static void -+ checkTableCommon(Oid table_oid, access_vector_t required) -+ { -+ HeapTuple tuple; -+ sepgsql_sid_t relSid; -+ uint16 tclass; -+ const char *auname; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(table_oid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation %u", table_oid); -+ -+ auname = NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname); -+ relSid = sepgsqlGetTupleContext(RelationRelationId, -+ tuple, &tclass); -+ sepgsqlClientHasPerms(relSid, tclass, required, -+ auname, true); -+ ReleaseSysCache(tuple); -+ } -+ -+ void -+ sepgsqlCheckTableDrop(Oid table_oid) -+ { -+ checkTableCommon(table_oid, SEPG_DB_TABLE__DROP); -+ } -+ -+ void -+ sepgsqlCheckTableSetattr(Oid table_oid) -+ { -+ checkTableCommon(table_oid, SEPG_DB_TABLE__SETATTR); -+ } -+ -+ Oid -+ sepgsqlCheckTableRelabel(Oid table_oid, DefElem *newLabel) -+ { -+ sepgsql_sid_t relSid; -+ char relkind; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ -+ relkind = get_rel_relkind(table_oid); -+ 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(table_oid)))); -+ -+ relSid.relid = RelationRelationId; -+ relSid.secid = securityTransSecLabelIn(relSid.relid, -+ strVal(newLabel->arg)); -+ -+ /* db_table/db_sequence:{setattr relabelfrom} for older seclabel */ -+ checkTableCommon(table_oid, -+ SEPG_DB_TABLE__SETATTR | -+ SEPG_DB_TABLE__RELABELFROM); -+ -+ /* db_table/db_sequence:{relabelto} for newer seclabel */ -+ sepgsqlClientHasPerms(relSid, -+ (relkind == RELKIND_RELATION -+ ? SEPG_CLASS_DB_TABLE -+ : SEPG_CLASS_DB_SEQUENCE), -+ SEPG_DB_TABLE__RELABELTO, -+ get_rel_name(table_oid), true); -+ return relSid.secid; -+ } -+ -+ void -+ sepgsqlCheckTableLock(Oid table_oid) -+ { -+ checkTableCommon(table_oid, SEPG_DB_TABLE__LOCK); -+ } -+ -+ void -+ sepgsqlCheckTableTruncate(Relation rel) -+ { -+ HeapScanDesc scan; -+ HeapTuple tuple; -+ sepgsql_sid_t tupSid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* check db_table:{delete} permission */ -+ checkTableCommon(RelationGetRelid(rel), SEPG_DB_TABLE__DELETE); -+ -+ /* 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) -+ { -+ tupSid = sepgsqlGetTupleContext(RelationGetRelid(rel), -+ tuple, &tclass); -+ sepgsqlClientHasPerms(tupSid, -+ tclass, SEPG_DB_TUPLE__DELETE, -+ NULL, true); -+ } -+ heap_endscan(scan); -+ } -+ -+ void -+ sepgsqlCheckTableReference(Relation rel, int16 *attnums, int natts) -+ { -+ int i; -+ -+ checkTableCommon(RelationGetRelid(rel), SEPG_DB_TABLE__REFERENCE); -+ -+ for (i=0; i < natts; i++) -+ { -+ checkColumnCommon(RelationGetRelid(rel), -+ attnums[i], SEPG_DB_COLUMN__REFERENCE); -+ } -+ } -+ -+ /* ------------------------------------------------------------ * -+ * Hooks corresponding to db_sequence object class -+ * ------------------------------------------------------------ */ -+ void sepgsqlCheckSequenceGetValue(Oid seqOid) -+ { -+ checkTableCommon(seqOid, SEPG_DB_SEQUENCE__GET_VALUE); -+ } -+ -+ void sepgsqlCheckSequenceNextValue(Oid seqOid) -+ { -+ checkTableCommon(seqOid, SEPG_DB_SEQUENCE__NEXT_VALUE); -+ } -+ -+ void sepgsqlCheckSequenceSetValue(Oid seqOid) -+ { -+ checkTableCommon(seqOid, SEPG_DB_SEQUENCE__SET_VALUE); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * Hooks corresponding to db_procedure object class -+ * ------------------------------------------------------------ */ -+ static bool -+ checkProcedureCommon(Oid procOid, uint32 required, bool abort) -+ { -+ sepgsql_sid_t proSid; -+ HeapTuple tuple; -+ uint16 tclass; -+ const char *auname; -+ bool rc; -+ -+ if (!sepgsqlIsEnabled()) -+ return true; -+ -+ 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); -+ proSid = sepgsqlGetTupleContext(ProcedureRelationId, -+ tuple, &tclass); -+ rc = sepgsqlClientHasPerms(proSid, tclass, required, -+ auname, abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+ } -+ -+ Oid -+ sepgsqlCheckProcedureCreate(const char *procName, Oid procOid, -+ Oid procNsp, Oid procLang, DefElem *newLabel) -+ { -+ sepgsql_sid_t proSid; -+ HeapTuple tuple; -+ uint32 required; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!OidIsValid(procOid)) -+ { -+ /* create a new function */ -+ required = SEPG_DB_PROCEDURE__CREATE; -+ if (!newLabel) -+ proSid = sepgsqlGetDefaultProcedureSecid(procNsp); -+ else -+ { -+ proSid.relid = ProcedureRelationId; -+ proSid.secid = securityTransSecLabelIn(proSid.relid, -+ strVal(newLabel->arg)); -+ } -+ } -+ else if (!newLabel) -+ { -+ /* replace an existing function, without any label */ -+ required = SEPG_DB_PROCEDURE__SETATTR; -+ tuple = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(procOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for procedure: %u", procOid); -+ -+ proSid = sepgsqlGetTupleContext(ProcedureRelationId, -+ tuple, NULL); -+ ReleaseSysCache(tuple); -+ } -+ else -+ { -+ /* replace an existing function, with relabeling */ -+ checkProcedureCommon(procOid, -+ SEPG_DB_PROCEDURE__SETATTR | -+ SEPG_DB_PROCEDURE__RELABELFROM, true); -+ -+ required = SEPG_DB_PROCEDURE__RELABELTO; -+ proSid.relid = ProcedureRelationId; -+ proSid.secid = securityTransSecLabelIn(proSid.relid, -+ strVal(newLabel->arg)); -+ } -+ -+ /* Procedural language is trusted? */ -+ tuple = SearchSysCache(LANGOID, -+ ObjectIdGetDatum(procLang), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for procedural langugage: %u", procLang); -+ -+ if (!((Form_pg_language) GETSTRUCT(tuple))->lanpltrusted) -+ required |= SEPG_DB_PROCEDURE__UNTRUSTED; -+ -+ ReleaseSysCache(tuple); -+ -+ /* check it */ -+ sepgsqlClientHasPerms(proSid, -+ SEPG_CLASS_DB_PROCEDURE, required, -+ procName, true); -+ -+ return proSid.secid; -+ } -+ -+ void -+ sepgsqlCheckProcedureDrop(Oid procOid) -+ { -+ checkProcedureCommon(procOid, SEPG_DB_PROCEDURE__DROP, true); -+ } -+ -+ void -+ sepgsqlCheckProcedureSetattr(Oid procOid) -+ { -+ checkProcedureCommon(procOid, SEPG_DB_PROCEDURE__SETATTR, true); -+ } -+ -+ Oid -+ sepgsqlCheckProcedureRelabel(Oid procOid, DefElem *newLabel) -+ { -+ sepgsql_sid_t proSid; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ -+ proSid.relid = ProcedureRelationId; -+ proSid.secid = securityTransSecLabelIn(proSid.relid, -+ strVal(newLabel->arg)); -+ -+ /* db_procedure:{setattr relabelfrom} for older seclabel */ -+ checkProcedureCommon(procOid, -+ SEPG_DB_PROCEDURE__SETATTR | -+ SEPG_DB_PROCEDURE__RELABELFROM, true); -+ /* db_procedure:{relabelto} for newer seclabel */ -+ sepgsqlClientHasPerms(proSid, -+ SEPG_CLASS_DB_PROCEDURE, -+ SEPG_DB_PROCEDURE__RELABELTO, -+ get_func_name(procOid), true); -+ return proSid.secid; -+ } -+ -+ void -+ sepgsqlCheckProcedureExecute(Oid procOid) -+ { -+ checkProcedureCommon(procOid, SEPG_DB_PROCEDURE__EXECUTE, true); -+ } -+ -+ /* -+ * sepgsqlCheckProcedureInstall -+ * -+ * It should be checked when a procedure is installed as a part of system -+ * internal stuff. -+ */ -+ void -+ sepgsqlCheckProcedureInstall(Oid procOid) -+ { -+ if (OidIsValid(procOid)) -+ checkProcedureCommon(procOid, SEPG_DB_PROCEDURE__INSTALL, true); -+ } -+ -+ /* -+ * sepgsqlHintProcedureInlined -+ * -+ * It provides a hint whether the given sql procedure can be inlined, or not. -+ */ -+ bool -+ sepgsqlHintProcedureInlined(HeapTuple protup) -+ { -+ security_context_t newcon; -+ sepgsql_sid_t proSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return true; -+ -+ if (!checkProcedureCommon(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. -+ */ -+ proSid = sepgsqlGetTupleContext(ProcedureRelationId, -+ protup, NULL); -+ -+ newcon = sepgsqlClientCreateLabel(proSid, SEPG_CLASS_PROCESS); -+ -+ if (strcmp(sepgsqlGetClientLabel(), newcon) == 0) -+ return true; -+ -+ return false; -+ } -+ -+ /* -+ * sepgsqlCheckProcedureEntrypoint -+ * checks whether the given function call causes domain transition, -+ * or not. If it needs a domain transition, it injects a wrapper -+ * function to invoke it under new domain. -+ */ -+ struct TrustedProcedureCache -+ { -+ FmgrInfo flinfo; -+ char newcon[1]; -+ }; -+ -+ static Datum -+ sepgsqlTrustedProcedure(PG_FUNCTION_ARGS) -+ { -+ struct TrustedProcedureCache *tcache; -+ security_context_t save_context; -+ FmgrInfo *save_flinfo; -+ Datum result; -+ -+ tcache = fcinfo->flinfo->fn_extra; -+ Assert(tcache != NULL); -+ -+ save_context = sepgsqlSwitchClient(tcache->newcon); -+ save_flinfo = fcinfo->flinfo; -+ fcinfo->flinfo = &tcache->flinfo; -+ -+ PG_TRY(); -+ { -+ result = FunctionCallInvoke(fcinfo); -+ } -+ PG_CATCH(); -+ { -+ sepgsqlSwitchClient(save_context); -+ fcinfo->flinfo = save_flinfo; -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ sepgsqlSwitchClient(save_context); -+ fcinfo->flinfo = save_flinfo; -+ -+ return result; -+ } -+ -+ void -+ sepgsqlCheckProcedureEntrypoint(FmgrInfo *flinfo, HeapTuple protup) -+ { -+ struct TrustedProcedureCache *tcache; -+ security_context_t newcon; -+ sepgsql_sid_t proSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ proSid = sepgsqlGetTupleContext(ProcedureRelationId, -+ protup, NULL); -+ -+ newcon = sepgsqlClientCreateLabel(proSid, SEPG_CLASS_PROCESS); -+ -+ /* Do nothing, if it is not a trusted procedure */ -+ if (strcmp(newcon, sepgsqlGetClientLabel()) == 0) -+ return; -+ -+ /* check db_procedure:{entrypoint} */ -+ sepgsqlClientHasPerms(proSid, -+ SEPG_CLASS_DB_PROCEDURE, -+ SEPG_DB_PROCEDURE__ENTRYPOINT, -+ NULL, true); -+ -+ /* check process:{transition} */ -+ sepgsqlComputePerms(sepgsqlGetClientLabel(), -+ newcon, -+ SEPG_CLASS_PROCESS, -+ SEPG_PROCESS__TRANSITION, -+ NULL, true); -+ -+ /* setup trusted procedure */ -+ tcache = MemoryContextAllocZero(flinfo->fn_mcxt, -+ sizeof(*tcache) + strlen(newcon)); -+ memcpy(&tcache->flinfo, flinfo, sizeof(*flinfo)); -+ strcpy(tcache->newcon, newcon); -+ flinfo->fn_addr = sepgsqlTrustedProcedure; -+ flinfo->fn_extra = tcache; -+ } -+ -+ /* + * sepgsqlCheckBlobCreate + * assigns a default security label and checks db_blob:{create} + */ @@ -9530,7 +12751,7 @@ diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/securit + /* set a default security context */ + sepgsqlSetDefaultSecid(rel, lotup); + -+ loSid = sepgsqlGetTupleContext(relid, lotup, NULL); ++ loSid = sepgsqlGetTupleSecid(relid, lotup, NULL); + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__CREATE, @@ -9550,7 +12771,7 @@ diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/securit + if (!sepgsqlIsEnabled()) + return; + -+ loSid = sepgsqlGetTupleContext(relid, lotup, NULL); ++ loSid = sepgsqlGetTupleSecid(relid, lotup, NULL); + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__DROP, @@ -9642,8 +12863,7 @@ diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/securit + * check db_blob:{read export} and file:{write} permission + */ + void -+ sepgsqlCheckBlobExport(LargeObjectDesc *lobj, -+ int fdesc, const char *filename) ++ sepgsqlCheckBlobExport(LargeObjectDesc *lobj, const char *filename) + { + sepgsql_sid_t loSid; + @@ -9658,7 +12878,7 @@ diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/securit + SEPG_DB_BLOB__READ | SEPG_DB_BLOB__EXPORT, + NULL, true); + /* file:{write} */ -+ sepgsqlCheckFileWrite(fdesc, filename); ++ sepgsql_file_write(filename); + } + + /* @@ -9666,8 +12886,7 @@ diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/securit + * check db_blob:{write import} and file:{read} permission + */ + void -+ sepgsqlCheckBlobImport(LargeObjectDesc *lobj, -+ int fdesc, const char *filename) ++ sepgsqlCheckBlobImport(LargeObjectDesc *lobj, const char *filename) + { + sepgsql_sid_t loSid; + @@ -9682,7 +12901,7 @@ diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/securit + SEPG_DB_BLOB__WRITE | SEPG_DB_BLOB__IMPORT, + NULL, true); + /* file:{read} */ -+ sepgsqlCheckFileRead(fdesc, filename); ++ sepgsql_file_read(filename); + } + + /* @@ -9699,7 +12918,7 @@ diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/securit + required |= SEPG_DB_BLOB__RELABELFROM; + + /* db_blob:{setattr relabelfrom} */ -+ loSid = sepgsqlGetTupleContext(LargeObjectRelationId, oldtup, NULL); ++ loSid = sepgsqlGetTupleSecid(LargeObjectRelationId, oldtup, NULL); + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + required, @@ -9709,164 +12928,18 @@ diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/securit + return; + + /* db_blob:{relabelto} */ -+ loSid = sepgsqlGetTupleContext(LargeObjectRelationId, newtup, NULL); ++ loSid = sepgsqlGetTupleSecid(LargeObjectRelationId, newtup, NULL); + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__RELABELTO, + NULL, true); + } -+ -+ /* -+ * sepgsqlCheckSysobjCreate -+ * -+ * It checks db_tuple:{insert} for system catalog -+ */ -+ Oid -+ sepgsqlCheckSysobjCreate(Oid relid, const char *auditName) -+ { -+ sepgsql_sid_t sysSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sysSid = sepgsqlGetDefaultTupleSecid(relid); -+ -+ sepgsqlClientHasPerms(sysSid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ auditName, true); -+ -+ return sysSid.secid; -+ } -+ -+ /* -+ * sepgsqlCheckSysobjGetattr -+ * -+ * It checks db_tuple:{select} for system catalog -+ */ -+ void -+ sepgsqlCheckSysobjGetattr(Oid relid, Oid secid, const char *auditName) -+ { -+ sepgsql_sid_t sysSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sysSid.relid = relid; -+ sysSid.secid = secid; -+ sepgsqlClientHasPerms(sysSid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__SELECT, -+ auditName, true); -+ } -+ -+ /* -+ * sepgsqlCheckSysobjSetattr -+ * -+ * It checks db_tuple:{update} for system catalog -+ */ -+ void -+ sepgsqlCheckSysobjSetattr(Oid relid, Oid secid, const char *auditName) -+ { -+ sepgsql_sid_t sysSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sysSid.relid = relid; -+ sysSid.secid = secid; -+ sepgsqlClientHasPerms(sysSid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__UPDATE, -+ auditName, true); -+ } -+ -+ /* -+ * sepgsqlCheckSysobjDrop -+ * It checks db_xxx:{drop} permission on the given opaque -+ * object, invoked from deleteOneObject() -+ */ -+ void -+ sepgsqlCheckSysobjDrop(const ObjectAddress *object) -+ { -+ switch (object->classId) -+ { -+ case NamespaceRelationId: -+ sepgsqlCheckSchemaDrop(object->objectId); -+ break; -+ -+ case RelationRelationId: -+ if (!object->objectSubId) -+ sepgsqlCheckTableDrop(object->objectId); -+ else -+ sepgsqlCheckColumnDrop(object->objectId, object->objectSubId); -+ break; -+ -+ case ProcedureRelationId: -+ sepgsqlCheckProcedureDrop(object->objectId); -+ break; -+ -+ default: -+ /* do nothing in this version */ -+ break; -+ } -+ } -+ -+ /* -+ * sepgsqlCheckFileRead -+ * sepgsqlCheckFileWrite -+ * check file:{read} or file:{write} permission on the given file, -+ * and raises an error if violated. -+ */ -+ static void -+ checkFileCommon(int fdesc, const char *filename, access_vector_t perms) -+ { -+ security_context_t context; -+ security_class_t tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tclass = sepgsqlFileObjectClass(fdesc); -+ -+ if (fgetfilecon_raw(fdesc, &context) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: could not get context of %s", filename))); -+ PG_TRY(); -+ { -+ sepgsqlComputePerms(sepgsqlGetClientLabel(), -+ context, -+ tclass, -+ perms, -+ filename, true); -+ } -+ PG_CATCH(); -+ { -+ freecon(context); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(context); -+ } -+ -+ void -+ sepgsqlCheckFileRead(int fdesc, const char *filename) -+ { -+ checkFileCommon(fdesc, filename, SEPG_FILE__READ); -+ } -+ -+ void -+ sepgsqlCheckFileWrite(int fdesc, const char *filename) -+ { -+ checkFileCommon(fdesc, filename, SEPG_FILE__WRITE); -+ } diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/security/sepgsql/label.c *** base/src/backend/security/sepgsql/label.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/label.c Thu Sep 10 15:18:03 2009 +--- sepgsql/src/backend/security/sepgsql/label.c Fri Sep 25 17:27:05 2009 *************** *** 0 **** ---- 1,1151 ---- +--- 1,1140 ---- + /* + * src/backend/security/sepgsql/label.c + * SE-PostgreSQL security label management @@ -9913,6 +12986,7 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + #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" @@ -10073,14 +13147,6 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + SEPG_CLASS_DB_SCHEMA); + } + -+ sepgsql_sid_t -+ sepgsqlGetDefaultSchemaTempSecid(Oid database_oid) -+ { -+ return defaultSecidWithDatabase(NamespaceRelationId, -+ database_oid, -+ SEPG_CLASS_DB_SCHEMA_TEMP); -+ } -+ + static sepgsql_sid_t + defaultSecidWithSchema(Oid relid, Oid nspoid, uint16 tclass) + { @@ -10196,9 +13262,10 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + void + sepgsqlSetDefaultSecid(Relation rel, HeapTuple tuple) + { -+ Oid relOid = RelationGetRelid(rel); -+ Oid nspOid, tblOid; + sepgsql_sid_t newSid; ++ Oid relOid = RelationGetRelid(rel); ++ Oid nspOid, tblOid; ++ char relkind; + + if (!sepgsqlIsEnabled()) + return; @@ -10206,282 +13273,85 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + if (!HeapTupleHasSecid(tuple)) + return; + -+ switch (sepgsqlTupleObjectClass(relOid, tuple)) ++ /* initialize */ ++ newSid.relid = relOid; ++ newSid.secid = InvalidOid; ++ ++ switch (relOid) + { -+ case SEPG_CLASS_DB_DATABASE: ++ case DatabaseRelationId: + newSid = sepgsqlGetDefaultDatabaseSecid(); + break; -+ case SEPG_CLASS_DB_SCHEMA: ++ ++ case NamespaceRelationId: + newSid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); + break; -+ case SEPG_CLASS_DB_SCHEMA_TEMP: -+ newSid = sepgsqlGetDefaultSchemaTempSecid(MyDatabaseId); -+ break; -+ case SEPG_CLASS_DB_TABLE: ++ ++ case RelationRelationId: + nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace; -+ newSid = sepgsqlGetDefaultTableSecid(nspOid); ++ 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 SEPG_CLASS_DB_SEQUENCE: -+ nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace; -+ newSid = sepgsqlGetDefaultSequenceSecid(nspOid); -+ break; -+ case SEPG_CLASS_DB_PROCEDURE: ++ ++ case ProcedureRelationId: + nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; + newSid = sepgsqlGetDefaultProcedureSecid(nspOid); + break; -+ case SEPG_CLASS_DB_COLUMN: ++ ++ case AttributeRelationId: + tblOid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; -+ newSid = sepgsqlGetDefaultColumnSecid(tblOid); ++ ++ /* ++ * We cannot refer system cache in the very early initdb ++ * phase, because pg_class is not constructed yet. ++ */ ++ if ((IsBootstrapProcessingMode() && ++ (tblOid == TypeRelationId || ++ tblOid == ProcedureRelationId || ++ tblOid == AttributeRelationId || ++ tblOid == RelationRelationId)) ++ || get_rel_relkind(tblOid) == RELKIND_RELATION) ++ { ++ newSid = sepgsqlGetDefaultColumnSecid(tblOid); ++ } ++ /* otherwise, it does not have individual security context */ + break; -+ case SEPG_CLASS_DB_BLOB: ++ ++ case LargeObjectRelationId: + newSid = sepgsqlGetDefaultBlobSecid(MyDatabaseId); + break; ++ + default: + newSid = sepgsqlGetDefaultTupleSecid(relOid); + break; + } + -+ Assert(newSid.relid == relOid); + HeapTupleSetSecid(tuple, newSid.secid); + } + + /* -+ * sepgsqlCreateTableColumn -+ * 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 * -+ sepgsqlCreateTableColumns(CreateStmt *stmt, -+ const char *relname, Oid namespace_oid, -+ TupleDesc tupdesc, char relkind) -+ { -+ sepgsql_sid_t relsid; -+ Oid *secLabels = NULL; -+ int index; -+ -+ if (!sepgsqlIsEnabled()) -+ return NULL; -+ -+ /* -+ * In the current version, we don't assign any certain security -+ * labels on relations except for tables/sequences. -+ */ -+ if (relkind != RELKIND_RELATION && relkind != RELKIND_SEQUENCE) -+ return NULL; -+ -+ /* -+ * The secLabels 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. -+ */ -+ secLabels = palloc0(sizeof(Oid) * (tupdesc->natts -+ - FirstLowInvalidHeapAttributeNumber)); -+ -+ /* -+ * SELinux checks db_table/db_sequence:{create} -+ */ -+ switch (relkind) -+ { -+ case RELKIND_RELATION: -+ if (!stmt || !stmt->secLabel) -+ relsid = sepgsqlGetDefaultTableSecid(namespace_oid); -+ else -+ { -+ relsid.relid = RelationRelationId; -+ relsid.secid = securityTransSecLabelIn(relsid.relid, -+ strVal(((DefElem *)stmt->secLabel)->arg)); -+ } -+ sepgsqlClientHasPerms(relsid, -+ SEPG_CLASS_DB_TABLE, -+ SEPG_DB_TABLE__CREATE, -+ relname, true); -+ break; -+ -+ case RELKIND_SEQUENCE: -+ if (!stmt || !stmt->secLabel) -+ relsid = sepgsqlGetDefaultSequenceSecid(namespace_oid); -+ else -+ { -+ relsid.relid = RelationRelationId; -+ relsid.secid = securityTransSecLabelIn(relsid.relid, -+ strVal(((DefElem *)stmt->secLabel)->arg)); -+ } -+ sepgsqlClientHasPerms(relsid, -+ SEPG_CLASS_DB_SEQUENCE, -+ SEPG_DB_SEQUENCE__CREATE, -+ relname, true); -+ break; -+ -+ default: -+ if (stmt && stmt->secLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", relname))); -+ relsid = sepgsqlGetDefaultTupleSecid(RelationRelationId); -+ break; -+ } -+ /* table's security identifier to be assigned on */ -+ secLabels[0] = relsid.secid; -+ -+ /* -+ * SELinux checks db_column:{create} -+ */ -+ for (index = FirstLowInvalidHeapAttributeNumber + 1; -+ index < tupdesc->natts; -+ index++) -+ { -+ Form_pg_attribute attr; -+ sepgsql_sid_t attsid = { InvalidOid, InvalidOid }; -+ char attname[NAMEDATALEN * 2 + 3]; -+ -+ /* skip unnecessary attributes */ -+ if (index < 0 && (relkind == RELKIND_VIEW || -+ relkind == RELKIND_COMPOSITE_TYPE)) -+ continue; -+ if (index == ObjectIdAttributeNumber && !tupdesc->tdhasoid) -+ continue; -+ -+ if (index < 0) -+ attr = SystemAttributeDefinition(index, tupdesc->tdhasoid); -+ else -+ attr = tupdesc->attrs[index]; -+ -+ /* Is there any given security label? */ -+ if (stmt) -+ { -+ ListCell *l; -+ -+ foreach (l, stmt->tableElts) -+ { -+ ColumnDef *colDef = lfirst(l); -+ -+ if (colDef->secLabel && -+ strcmp(colDef->colname, NameStr(attr->attname)) == 0) -+ { -+ attsid.relid = AttributeRelationId; -+ attsid.secid = securityTransSecLabelIn(attsid.relid, -+ strVal(((DefElem *)colDef->secLabel)->arg)); -+ break; -+ } -+ } -+ } -+ -+ switch (relkind) -+ { -+ case RELKIND_RELATION: -+ /* compute default column's label if necessary */ -+ if (!SidIsValid(attsid)) -+ attsid = sepgsqlClientCreateSecid(relsid, -+ SEPG_CLASS_DB_COLUMN, -+ AttributeRelationId); -+ -+ sprintf(attname, "%s.%s", relname, NameStr(attr->attname)); -+ sepgsqlClientHasPerms(attsid, -+ SEPG_CLASS_DB_COLUMN, -+ SEPG_DB_COLUMN__CREATE, -+ attname, true); -+ break; -+ -+ default: -+ if (SidIsValid(attsid)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s.%s\"", -+ relname, NameStr(attr->attname)))); -+ attsid = sepgsqlGetDefaultTupleSecid(AttributeRelationId); -+ break; -+ } -+ /* column's security identifier to be assigend on */ -+ secLabels[index - FirstLowInvalidHeapAttributeNumber] = attsid.secid; -+ } -+ return secLabels; -+ } -+ -+ /* -+ * sepgsqlCopyTableColumns -+ * 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 * -+ sepgsqlCopyTableColumns(Relation source) -+ { -+ HeapTuple tuple; -+ Oid *secLabels; -+ Oid relid = RelationGetRelid(source); -+ int index; -+ -+ if (!sepgsqlIsEnabled()) -+ return PointerGetDatum(NULL); -+ -+ /* see the comment at sepgsqlCreateTableColumn*/ -+ secLabels = palloc0(sizeof(Oid) * (RelationGetDescr(source)->natts -+ - FirstLowInvalidHeapAttributeNumber)); -+ -+ /* copy table's security identifier */ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation \"%s\"", -+ RelationGetRelationName(source)); -+ -+ secLabels[0] = HeapTupleGetSecid(tuple); -+ -+ ReleaseSysCache(tuple); -+ -+ /* copy column's security identifier */ -+ for (index = FirstLowInvalidHeapAttributeNumber + 1; -+ index < RelationGetDescr(source)->natts; -+ index++) -+ { -+ Form_pg_attribute attr; -+ -+ if (index < 0) -+ attr = SystemAttributeDefinition(index, true); -+ else -+ attr = RelationGetDescr(source)->attrs[index]; -+ -+ tuple = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(relid), -+ Int16GetDatum(attr->attnum), -+ 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ continue; -+ -+ secLabels[index - FirstLowInvalidHeapAttributeNumber] -+ = HeapTupleGetSecid(tuple); -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ return secLabels; -+ } -+ -+ /* -+ * sepgsqlGetSysobjContext ++ * sepgsqlGetSysobjSecid + * + * It returns a pair of relid/secid for the given OID. + */ + static sepgsql_sid_t -+ getSysobjContextDirect(Oid classOid, Oid indexOid, Oid objectId, uint16 *tclass) ++ getSysobjSecidDirect(Oid classOid, Oid indexOid, Oid objectId, uint16 *tclass) + { -+ sepgsql_sid_t sid = { InvalidOid, InvalidOid }; ++ sepgsql_sid_t sid; + Relation rel; + HeapTuple tup; + ScanKeyData skey; @@ -10498,8 +13368,11 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + SnapshotNow, 1, &skey); + tup = systable_getnext(scan); + -+ if (HeapTupleIsValid(tup)) -+ sid = sepgsqlGetTupleContext(classOid, tup, tclass); ++ 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); + @@ -10509,10 +13382,10 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + } + + sepgsql_sid_t -+ sepgsqlGetSysobjContext(Oid classOid, Oid objectId, int32 objsubId, uint16 *tclass) ++ sepgsqlGetSysobjSecid(Oid classOid, Oid objectId, int32 objsubId, uint16 *tclass) + { -+ sepgsql_sid_t sid = { InvalidOid, InvalidOid }; -+ HeapTuple tup = NULL; ++ sepgsql_sid_t sid; ++ HeapTuple tup; + + switch (classOid) + { @@ -10520,57 +13393,71 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + 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 getSysobjContextDirect(AccessMethodOperatorRelationId, -+ AccessMethodOperatorOidIndexId, -+ objectId, tclass); ++ return getSysobjSecidDirect(AccessMethodOperatorRelationId, ++ AccessMethodOperatorOidIndexId, ++ objectId, tclass); + + case AccessMethodProcedureRelationId: -+ return getSysobjContextDirect(AccessMethodProcedureRelationId, -+ AccessMethodProcedureOidIndexId, -+ objectId, tclass); ++ 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 getSysobjContextDirect(CastRelationId, -+ CastOidIndexId, -+ objectId, tclass); ++ 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: @@ -10598,9 +13485,10 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + + tup = systable_getnext(scan); + -+ if (HeapTupleIsValid(tup)) -+ sid = sepgsqlGetTupleContext(classOid, tup, tclass); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "largeobject %u lookup failed", objectId); + ++ sid = sepgsqlGetTupleSecid(classOid, tup, tclass); + systable_endscan(scan); + + heap_close(rel, AccessShareLock); @@ -10615,6 +13503,9 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + ObjectIdGetDatum(objectId), + Int16GetDatum(objsubId), + 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for attribute %d of relation %u", ++ objsubId, objectId); + } + else + { @@ -10622,6 +13513,8 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + tup = SearchSysCache(RELOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for relation %u", objectId); + } + break; + @@ -10629,99 +13522,122 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + 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 getSysobjContextDirect(RewriteRelationId, -+ RewriteOidIndexId, -+ objectId, tclass); ++ return getSysobjSecidDirect(RewriteRelationId, ++ RewriteOidIndexId, ++ objectId, tclass); + + case TableSpaceRelationId: -+ return getSysobjContextDirect(TableSpaceRelationId, -+ TablespaceOidIndexId, -+ objectId, tclass); ++ return getSysobjSecidDirect(TableSpaceRelationId, ++ TablespaceOidIndexId, ++ objectId, tclass); + + case TriggerRelationId: -+ return getSysobjContextDirect(TriggerRelationId, -+ TriggerOidIndexId, -+ objectId, tclass); ++ 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; + } -+ -+ if (HeapTupleIsValid(tup)) -+ { -+ sid = sepgsqlGetTupleContext(classOid, tup, tclass); -+ ReleaseSysCache(tup); -+ } ++ ++ Assert(HeapTupleIsValid(tup)); ++ ++ sid = sepgsqlGetTupleSecid(classOid, tup, tclass); ++ ++ ReleaseSysCache(tup); + + return sid; + } + + /* -+ * sepgsqlGetTupleContext ++ * sepgsqlGetTupleSecid + * + * It returns a pair of relid/secid for the given HeapTuple. + * A few system catalogs is handled as an attribute of other @@ -10729,59 +13645,109 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + * E.g) pg_attrdef is an attribute of a certain pg_attribute + */ + sepgsql_sid_t -+ sepgsqlGetTupleContext(Oid tableOid, HeapTuple tuple, uint16 *tclass) ++ sepgsqlGetTupleSecid(Oid tableOid, HeapTuple tuple, uint16 *tclass) + { -+ sepgsql_sid_t sid = { InvalidOid, InvalidOid }; ++ 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: -+ sid.relid = ProcedureRelationId; + 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: -+ sid.relid = OperatorFamilyRelationId; + 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: -+ sid.relid = OperatorFamilyRelationId; + 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: -+ sid.relid = AttributeRelationId; + 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: -+ sid.relid = AuthIdRelationId; + 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: @@ -10789,10 +13755,15 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + extid = ((Form_pg_constraint) GETSTRUCT(tuple))->conrelid; + if (OidIsValid(extid)) + { -+ sid.relid = RelationRelationId; + 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 */ @@ -10803,93 +13774,184 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + 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 */ -+ sid.relid = DatabaseRelationId; + 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 sepgsqlGetSysobjContext(extcls, extid, 0, tclass); ++ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass); + + case EnumRelationId: -+ sid.relid = TypeRelationId; + 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: -+ sid.relid = RelationRelationId; -+ extid = ((Form_pg_index) GETSTRUCT(tuple))->indexrelid; ++ 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: -+ sid.relid = RelationRelationId; + extid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid; + exttup = SearchSysCache(RELOID, + ObjectIdGetDatum(extid), + 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(RelationRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case LargeObjectRelationId: ++ sid.secid = HeapTupleGetSecid(tuple); ++ if (tclass) ++ *tclass = SEPG_CLASS_DB_BLOB; ++ break; ++ ++ case NamespaceRelationId: ++ sid.secid = HeapTupleGetSecid(tuple); ++ if (tclass) ++ *tclass = SEPG_CLASS_DB_SCHEMA; ++ break; ++ ++ case ProcedureRelationId: ++ sid.secid = HeapTupleGetSecid(tuple); ++ if (tclass) ++ *tclass = SEPG_CLASS_DB_PROCEDURE; ++ break; ++ ++ case RelationRelationId: ++ sid.secid = HeapTupleGetSecid(tuple); ++ if (tclass) ++ { ++ char relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind; ++ ++ switch (relkind) ++ { ++ case RELKIND_RELATION: ++ *tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ ++ case RELKIND_SEQUENCE: ++ *tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ ++ default: ++ *tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ } ++ } + break; + + case RewriteRelationId: -+ sid.relid = RelationRelationId; + 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 sepgsqlGetSysobjContext(extcls, extid, 0, tclass); ++ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass); + + case StatisticRelationId: -+ sid.relid = AttributeRelationId; + 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: -+ sid.relid = RelationRelationId; + 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: -+ exttup = tuple; -+ sid.relid = tableOid; ++ /* No external lookups (normal case) */ ++ sid.secid = HeapTupleGetSecid(tuple); + break; + } + -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid.secid = HeapTupleGetSecid(exttup); -+ -+ if (tclass) -+ *tclass = sepgsqlTupleObjectClass(sid.relid, exttup); -+ -+ if (exttup != tuple) -+ ReleaseSysCache(exttup); -+ } + return sid; + } + @@ -11014,7 +14076,7 @@ diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/securit + { + sepgsql_sid_t sid; + -+ sid = sepgsqlGetTupleContext(relid, tuple, NULL); ++ sid = sepgsqlGetTupleSecid(relid, tuple, NULL); + + return securityTransSecLabelOut(sid.relid, sid.secid); + } @@ -11330,10 +14392,10 @@ diff -Nrpc base/src/backend/security/sepgsql/misc.c sepgsql/src/backend/security + } diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/security/sepgsql/perms.c *** base/src/backend/security/sepgsql/perms.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/perms.c Sun Sep 6 19:31:39 2009 +--- sepgsql/src/backend/security/sepgsql/perms.c Mon Sep 28 09:29:32 2009 *************** *** 0 **** ---- 1,464 ---- +--- 1,597 ---- + /* + * src/backend/utils/sepgsql/perms.c + * SE-PostgreSQL permission checks @@ -11381,6 +14443,8 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + { + {"read", SEPG_FILE__READ }, + {"write", SEPG_FILE__WRITE }, ++ {"create", SEPG_FILE__CREATE }, ++ {"getattr", SEPG_FILE__GETATTR }, + {NULL, 0} + } + }, @@ -11389,6 +14453,8 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + { + {"read", SEPG_DIR__READ }, + {"write", SEPG_DIR__WRITE }, ++ {"create", SEPG_DIR__CREATE }, ++ {"getattr", SEPG_DIR__GETATTR }, + {NULL,0} + } + }, @@ -11397,6 +14463,8 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + { + {"read", SEPG_LNK_FILE__READ }, + {"write", SEPG_LNK_FILE__WRITE }, ++ {"create", SEPG_LNK_FILE__CREATE }, ++ {"getattr", SEPG_LNK_FILE__GETATTR }, + {NULL,0} + } + }, @@ -11405,6 +14473,8 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + { + {"read", SEPG_CHR_FILE__READ }, + {"write", SEPG_CHR_FILE__WRITE }, ++ {"create", SEPG_CHR_FILE__CREATE }, ++ {"getattr", SEPG_CHR_FILE__GETATTR }, + {NULL,0} + } + }, @@ -11413,6 +14483,8 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + { + {"read", SEPG_BLK_FILE__READ }, + {"write", SEPG_BLK_FILE__WRITE }, ++ {"create", SEPG_BLK_FILE__CREATE }, ++ {"getattr", SEPG_BLK_FILE__GETATTR }, + {NULL,0} + } + }, @@ -11421,6 +14493,8 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + { + {"read", SEPG_SOCK_FILE__READ }, + {"write", SEPG_SOCK_FILE__WRITE }, ++ {"create", SEPG_SOCK_FILE__CREATE }, ++ {"getattr", SEPG_SOCK_FILE__GETATTR }, + {NULL,0} + } + }, @@ -11429,6 +14503,8 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + { + {"read", SEPG_FIFO_FILE__READ }, + {"write", SEPG_FIFO_FILE__WRITE }, ++ {"create", SEPG_FIFO_FILE__CREATE }, ++ {"getattr", SEPG_FIFO_FILE__GETATTR }, + {NULL, 0UL } + } + }, @@ -11464,21 +14540,6 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + } + }, + { -+ "db_schema_temp", SEPG_CLASS_DB_SCHEMA_TEMP, -+ { -+ { "create", SEPG_DB_SCHEMA_TEMP__CREATE }, -+ { "drop", SEPG_DB_SCHEMA_TEMP__DROP}, -+ { "getattr", SEPG_DB_SCHEMA_TEMP__GETATTR }, -+ { "setattr", SEPG_DB_SCHEMA_TEMP__SETATTR }, -+ { "relabelfrom", SEPG_DB_SCHEMA_TEMP__RELABELFROM }, -+ { "relabelto", SEPG_DB_SCHEMA_TEMP__RELABELTO }, -+ { "search", SEPG_DB_SCHEMA_TEMP__SEARCH }, -+ { "add_name", SEPG_DB_SCHEMA_TEMP__ADD_NAME }, -+ { "remove_name", SEPG_DB_SCHEMA_TEMP__REMOVE_NAME }, -+ { NULL, 0UL }, -+ } -+ }, -+ { + "db_table", SEPG_CLASS_DB_TABLE, + { + { "create", SEPG_DB_TABLE__CREATE }, @@ -11579,7 +14640,7 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + * external code which is necessary to communicate in-kernel SELinux + */ + extern security_class_t -+ sepgsqlTransToExternalClass(security_class_t tclass) ++ sepgsqlTransToExternalClass(uint16 tclass) + { + Assert(tclass < SEPG_CLASS_MAX); + @@ -11666,52 +14727,7 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + return NULL; + } + -+ /* -+ * sepgsqlAuditName -+ * returns an identifier string to generate audit record for -+ * the given tuple. Please note that its results can indicate -+ * an address within the given tuple, so we should not refer -+ * the returned pointer after HeapTuple is released. -+ */ -+ const char * -+ sepgsqlAuditName(Oid relid, HeapTuple tuple) -+ { -+ static char buffer[NAMEDATALEN * 2 + 10]; -+ -+ switch (relid) -+ { -+ case DatabaseRelationId: -+ return NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); -+ -+ case NamespaceRelationId: -+ return NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); -+ -+ case RelationRelationId: -+ return NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname); -+ -+ case AttributeRelationId: -+ if (!IsBootstrapProcessingMode()) -+ { -+ Form_pg_attribute attForm -+ = (Form_pg_attribute) GETSTRUCT(tuple); -+ char *relname -+ = get_rel_name(attForm->attrelid); -+ -+ if (relname) -+ { -+ snprintf(buffer, sizeof(buffer), "%s.%s", -+ relname, NameStr(attForm->attname)); -+ pfree(relname); -+ return buffer; -+ } -+ } -+ return NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname); -+ -+ case ProcedureRelationId: -+ return NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); -+ } -+ return NULL; -+ } ++ #if 0 + + /* + * sepgsqlFileObjectClass @@ -11719,7 +14735,7 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + * It returns proper object class of filesystem object already opened. + * It is necessary to check privileges voluntarily. + */ -+ security_class_t ++ uint16 + sepgsqlFileObjectClass(int fdesc) + { + struct stat stbuf; @@ -11750,10 +14766,9 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + * + * It returns correct object class of given tuple + */ -+ security_class_t ++ uint16 + sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple) + { -+ Form_pg_namespace nspForm; + Form_pg_class clsForm; + Form_pg_attribute attForm; + @@ -11763,10 +14778,6 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + return SEPG_CLASS_DB_DATABASE; + + case NamespaceRelationId: -+ nspForm = (Form_pg_namespace) GETSTRUCT(tuple); -+ if (strncmp(NameStr(nspForm->nspname), "pg_temp_", 8) == 0 || -+ strncmp(NameStr(nspForm->nspname), "pg_toast_temp_", 14) == 0) -+ return SEPG_CLASS_DB_SCHEMA_TEMP; + return SEPG_CLASS_DB_SCHEMA; + + case RelationRelationId: @@ -11798,6 +14809,190 @@ diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/securit + } + return SEPG_CLASS_DB_TUPLE; + } ++ ++ /* ++ * sepgsqlTupleNamespace ++ * ++ * It returns an OID of the namespace, if the given system object is ++ * deployed under a certain namespace. ++ */ ++ Oid ++ sepgsqlTupleNamespace(Oid relOid, HeapTuple tuple) ++ { ++ Oid nspOid; ++ ++ switch (relOid) ++ { ++ case RelationRelationId: ++ nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace; ++ break; ++ ++ case ConstraintRelationId: ++ nspOid = ((Form_pg_constraint) GETSTRUCT(tuple))->connamespace; ++ break; ++ ++ case ConversionRelationId: ++ nspOid = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace; ++ break; ++ ++ case OperatorClassRelationId: ++ nspOid = ((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace; ++ break; ++ ++ case OperatorRelationId: ++ nspOid = ((Form_pg_operator) GETSTRUCT(tuple))->oprnamespace; ++ break; ++ ++ case OperatorFamilyRelationId: ++ nspOid = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace; ++ break; ++ ++ case ProcedureRelationId: ++ nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; ++ break; ++ ++ case TSConfigRelationId: ++ nspOid = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace; ++ break; ++ ++ case TSDictionaryRelationId: ++ nspOid = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace; ++ break; ++ ++ case TSParserRelationId: ++ nspOid = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace; ++ break; ++ ++ case TSTemplateRelationId: ++ nspOid = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace; ++ break; ++ ++ default: ++ /* no specific namespace */ ++ nspOid = InvalidOid; ++ break; ++ } ++ ++ return nspOid; ++ } ++ ++ /* ++ * sepgsqlTupleAuditName ++ * ++ * It returns an OID of the namespace, if the given system object is ++ * deployed under a certain namespace. ++ */ ++ void ++ sepgsqlTupleAuditName(Oid relid, HeapTuple tuple, char *auname_buf) ++ { ++ char *name; ++ Oid extid; ++ ++ switch (relid) ++ { ++ case AccessMethodRelationId: ++ name = NameStr(((Form_pg_am) GETSTRUCT(tuple))->amname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case AttributeRelationId: ++ name = NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname); ++ extid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; ++ sprintf(audit_name, "%s.%s", name, extid); ++ return; ++ ++ case AuthIdRelationId: ++ name = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case ConversionRelationId: ++ name = NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case DatabaseRelationId: ++ name = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case ForeignDataWrapperRelationId: ++ name = NameStr(((Form_pg_foreign_data_wrapper) GETSTRUCT(tuple))->fdwname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case ForeignServerRelationId: ++ name = NameStr(((Form_pg_foreign_server) GETSTRUCT(tuple))->srvname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case LanguageRelationId: ++ name = NameStr(((Form_pg_language) GETSTRUCT(tuple))->lanname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case NamespaceRelationId: ++ name = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case OperatorClassRelationId: ++ name = NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case OperatorRelationId: ++ name = NameStr(((Form_pg_operator) GETSTRUCT(tuple))->oprname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case OperatorFamilyRelationId: ++ name = NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case ProcedureRelationId: ++ name = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case RelationRelationId: ++ name = NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TableSpaceRelationId: ++ name = NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TSConfigRelationId: ++ name = NameStr(((Form_pg_ts_config) GETSTRUCT(tuple))->cfgname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TSDictionaryRelationId: ++ name = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TSParserRelationId: ++ name = NameStr(((Form_pg_ts_parser) GETSTRUCT(tuple))->prsname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TSTemplateRelationId: ++ name = NameStr(((Form_pg_templace) GETSTRUCT(tuple))->tmplname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ default: ++ /* no auditable name */ ++ auname_buf[0] = '\0'; ++ break; ++ } ++ } ++ #endif diff -Nrpc base/src/backend/security/sepgsql/policy/Makefile sepgsql/src/backend/security/sepgsql/policy/Makefile *** base/src/backend/security/sepgsql/policy/Makefile Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/policy/Makefile Wed Jul 15 19:35:52 2009 @@ -11852,11 +15047,11 @@ diff -Nrpc base/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.templa + diff -Nrpc base/src/backend/security/sepgsql/policy/sepostgresql-devel.te sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.te *** base/src/backend/security/sepgsql/policy/sepostgresql-devel.te Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.te Thu Sep 10 15:43:44 2009 +--- sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.te Tue Dec 1 17:11:40 2009 *************** *** 0 **** ---- 1,121 ---- -+ policy_module(sepostgresql-devel, 3.28) +--- 1,123 ---- ++ policy_module(sepostgresql-devel, 3.29) + + gen_require(` + class db_database all_db_database_perms; @@ -11879,10 +15074,9 @@ diff -Nrpc base/src/backend/security/sepgsql/policy/sepostgresql-devel.te sepgsq + # for regression test + type bin_t; + type user_home_t; -+ type sepgsql_trusted_proc_t; ++ type sepgsql_trusted_proc_exec_t; + + attribute tmpfile; -+ attribute ptynode; + ') + + ################################# @@ -11897,7 +15091,10 @@ diff -Nrpc base/src/backend/security/sepgsql/policy/sepostgresql-devel.te sepgsq + + allow sepgsql_test_t tmpfile : dir search_dir_perms; + allow sepgsql_test_t tmpfile : file rw_file_perms; -+ allow sepgsql_test_t ptynode : chr_file rw_file_perms; ++ ++ optional_policy(` ++ term_write_all_terms(sepgsql_test_t) ++ ') + + optional_policy(` + gen_require(` @@ -11908,7 +15105,7 @@ diff -Nrpc base/src/backend/security/sepgsql/policy/sepostgresql-devel.te sepgsq + tunable_policy(`sepgsql_regression_test_mode',` + allow unconfined_t sepgsql_test_t : process transition; + ') -+ allow sepgsql_test_t unconfined_t : fifo_file read_file_perms; ++ unconfined_rw_pipes(sepgsql_test_t) + role unconfined_r types sepgsql_test_t; + role unconfined_r types sepgsql_trusted_proc_t; + ') @@ -12251,7 +15448,7 @@ diff -Nrpc base/src/backend/storage/large_object/inv_api.c sepgsql/src/backend/s + } diff -Nrpc base/src/backend/tcop/fastpath.c sepgsql/src/backend/tcop/fastpath.c *** base/src/backend/tcop/fastpath.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/tcop/fastpath.c Mon Sep 7 00:55:45 2009 +--- sepgsql/src/backend/tcop/fastpath.c Thu Sep 17 17:04:16 2009 *************** *** 26,31 **** --- 26,32 ---- @@ -12268,13 +15465,13 @@ diff -Nrpc base/src/backend/tcop/fastpath.c sepgsql/src/backend/tcop/fastpath.c if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(fip->namespace)); -+ sepgsqlCheckSchemaSearch(fip->namespace, true); ++ 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)); -+ sepgsqlCheckProcedureExecute(fid); ++ sepgsql_proc_execute(fid); /* * Prepare function call info block and insert arguments. @@ -12393,7 +15590,7 @@ diff -Nrpc base/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c break; diff -Nrpc base/src/backend/utils/adt/genfile.c sepgsql/src/backend/utils/adt/genfile.c *** base/src/backend/utils/adt/genfile.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/utils/adt/genfile.c Wed Jul 15 19:48:58 2009 +--- sepgsql/src/backend/utils/adt/genfile.c Mon Sep 28 09:29:32 2009 *************** *** 24,29 **** --- 24,30 ---- @@ -12405,16 +15602,27 @@ diff -Nrpc base/src/backend/utils/adt/genfile.c sepgsql/src/backend/utils/adt/ge #include "utils/builtins.h" #include "utils/memutils.h" *************** pg_read_file(PG_FUNCTION_ARGS) -*** 105,110 **** ---- 106,114 ---- - errmsg("could not open file \"%s\" for reading: %m", - filename))); +*** 99,104 **** +--- 100,108 ---- + + filename = convert_and_check_filename(filename_t); + /* SELinux: check file:{read} permission */ -+ sepgsqlCheckFileRead(fileno(file), filename); ++ sepgsql_file_read(filename); + - if (fseeko(file, (off_t) seek_offset, - (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0) + if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL) + ereport(ERROR, + (errcode_for_file_access(), +*************** pg_stat_file(PG_FUNCTION_ARGS) +*** 159,164 **** +--- 163,170 ---- + (errmsg("must be superuser to get file information")))); + + filename = convert_and_check_filename(filename_t); ++ /* SELinux: check file:{getattr} permission */ ++ sepgsql_file_stat(filename); + + if (stat(filename, &fst) < 0) ereport(ERROR, diff -Nrpc base/src/backend/utils/adt/ri_triggers.c sepgsql/src/backend/utils/adt/ri_triggers.c *** base/src/backend/utils/adt/ri_triggers.c Thu Jun 18 10:20:52 2009 @@ -12497,6 +15705,31 @@ diff -Nrpc base/src/backend/utils/adt/ri_triggers.c sepgsql/src/backend/utils/ad /* Restore UID */ SetUserIdAndContext(save_userid, save_secdefcxt); +diff -Nrpc base/src/backend/utils/adt/tid.c sepgsql/src/backend/utils/adt/tid.c +*** base/src/backend/utils/adt/tid.c Sat Jan 3 13:01:35 2009 +--- sepgsql/src/backend/utils/adt/tid.c Fri Sep 18 14:51:00 2009 +*************** currtid_byreloid(PG_FUNCTION_ARGS) +*** 347,352 **** +--- 347,354 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_relation_get_transaction_id(RelationGetRelid(rel)); + + if (rel->rd_rel->relkind == RELKIND_VIEW) + return currtid_for_view(rel, tid); +*************** currtid_byrelname(PG_FUNCTION_ARGS) +*** 377,382 **** +--- 379,386 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_relation_get_transaction_id(RelationGetRelid(rel)); + + if (rel->rd_rel->relkind == RELKIND_VIEW) + return currtid_for_view(rel, tid); diff -Nrpc base/src/backend/utils/adt/trigfuncs.c sepgsql/src/backend/utils/adt/trigfuncs.c *** base/src/backend/utils/adt/trigfuncs.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/utils/adt/trigfuncs.c Tue Sep 8 23:55:48 2009 @@ -12701,7 +15934,7 @@ diff -Nrpc base/src/backend/utils/cache/syscache.c sepgsql/src/backend/utils/cac --- 956,961 ---- diff -Nrpc base/src/backend/utils/fmgr/dfmgr.c sepgsql/src/backend/utils/fmgr/dfmgr.c *** base/src/backend/utils/fmgr/dfmgr.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/utils/fmgr/dfmgr.c Sun Sep 6 19:53:10 2009 +--- sepgsql/src/backend/utils/fmgr/dfmgr.c Thu Sep 17 17:04:16 2009 *************** *** 23,28 **** --- 23,29 ---- @@ -12719,7 +15952,7 @@ diff -Nrpc base/src/backend/utils/fmgr/dfmgr.c sepgsql/src/backend/utils/fmgr/df fullname = expand_dynamic_library_name(filename); + /* SELinux checks db_database:{load_module} */ -+ sepgsqlCheckDatabaseLoadModule(fullname); ++ sepgsql_database_load_module(MyDatabaseId, fullname); + /* Load the shared library, unless we already did */ lib_handle = internal_load_library(fullname); @@ -12731,14 +15964,14 @@ diff -Nrpc base/src/backend/utils/fmgr/dfmgr.c sepgsql/src/backend/utils/fmgr/df fullname = expand_dynamic_library_name(filename); + /* SELinux checks db_database:{load_module} */ -+ sepgsqlCheckDatabaseLoadModule(fullname); ++ sepgsql_database_load_module(MyDatabaseId, fullname); + /* Unload the library if currently loaded */ internal_unload_library(fullname); diff -Nrpc base/src/backend/utils/fmgr/fmgr.c sepgsql/src/backend/utils/fmgr/fmgr.c *** base/src/backend/utils/fmgr/fmgr.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/utils/fmgr/fmgr.c Wed Jul 15 19:35:52 2009 +--- sepgsql/src/backend/utils/fmgr/fmgr.c Thu Sep 17 17:04:16 2009 *************** *** 24,29 **** --- 24,30 ---- @@ -12755,13 +15988,13 @@ diff -Nrpc base/src/backend/utils/fmgr/fmgr.c sepgsql/src/backend/utils/fmgr/fmg } finfo->fn_oid = functionId; -+ sepgsqlCheckProcedureEntrypoint(finfo, procedureTuple); ++ sepgsql_proc_entrypoint(finfo, procedureTuple); ReleaseSysCache(procedureTuple); } diff -Nrpc base/src/backend/utils/init/postinit.c sepgsql/src/backend/utils/init/postinit.c *** base/src/backend/utils/init/postinit.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/utils/init/postinit.c Mon Sep 7 00:55:45 2009 +--- sepgsql/src/backend/utils/init/postinit.c Thu Sep 17 17:04:16 2009 *************** *** 32,37 **** --- 32,38 ---- @@ -12796,7 +16029,7 @@ diff -Nrpc base/src/backend/utils/init/postinit.c sepgsql/src/backend/utils/init errdetail("User does not have CONNECT privilege."))); + /* SELinux: db_database:{access} */ -+ sepgsqlCheckDatabaseAccess(MyDatabaseId); ++ sepgsql_database_access(MyDatabaseId); + /* * Check connection limit for this database. @@ -12863,7 +16096,7 @@ diff -Nrpc base/src/backend/utils/misc/guc.c sepgsql/src/backend/utils/misc/guc. { diff -Nrpc base/src/backend/utils/misc/postgresql.conf.sample sepgsql/src/backend/utils/misc/postgresql.conf.sample *** base/src/backend/utils/misc/postgresql.conf.sample Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/utils/misc/postgresql.conf.sample Thu Sep 10 00:06:47 2009 +--- sepgsql/src/backend/utils/misc/postgresql.conf.sample Tue Dec 1 17:11:40 2009 *************** *** 51,57 **** @@ -12890,18 +16123,17 @@ diff -Nrpc base/src/backend/utils/misc/postgresql.conf.sample sepgsql/src/backen #------------------------------------------------------------------------------ # RESOURCE USAGE (except WAL) ---- 95,102 ---- +--- 95,101 ---- # 0 selects the system default #tcp_keepalives_count = 0 # TCP_KEEPCNT; # 0 selects the system default ! #sepostgresql = off # SE-PostgreSQL support -! sepostgresql = on #------------------------------------------------------------------------------ # RESOURCE USAGE (except WAL) diff -Nrpc base/src/backend/utils/misc/superuser.c sepgsql/src/backend/utils/misc/superuser.c *** base/src/backend/utils/misc/superuser.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/utils/misc/superuser.c Wed Jul 15 19:35:52 2009 +--- sepgsql/src/backend/utils/misc/superuser.c Thu Sep 17 17:04:16 2009 *************** *** 21,26 **** --- 21,27 ---- @@ -12913,7 +16145,7 @@ diff -Nrpc base/src/backend/utils/misc/superuser.c sepgsql/src/backend/utils/mis #include "utils/syscache.h" #include "miscadmin.h" *************** superuser_arg(Oid roleid) -*** 60,70 **** +*** 60,66 **** /* Quick out for cache hit */ if (OidIsValid(last_roleid) && last_roleid == roleid) @@ -12921,11 +16153,7 @@ diff -Nrpc base/src/backend/utils/misc/superuser.c sepgsql/src/backend/utils/mis /* Special escape path in case you deleted all your users. */ if (!IsUnderPostmaster && roleid == BOOTSTRAP_SUPERUSERID) -! return true; - - /* OK, look up the information in pg_authid */ - rtup = SearchSysCache(AUTHOID, ---- 61,77 ---- +--- 61,70 ---- /* Quick out for cache hit */ if (OidIsValid(last_roleid) && last_roleid == roleid) @@ -12936,22 +16164,15 @@ diff -Nrpc base/src/backend/utils/misc/superuser.c sepgsql/src/backend/utils/mis /* Special escape path in case you deleted all your users. */ if (!IsUnderPostmaster && roleid == BOOTSTRAP_SUPERUSERID) -! { -! result = true; -! goto out; -! } - - /* OK, look up the information in pg_authid */ - rtup = SearchSysCache(AUTHOID, *************** superuser_arg(Oid roleid) *** 94,99 **** ---- 101,110 ---- +--- 98,107 ---- last_roleid = roleid; last_roleid_is_super = result; + out: + if (result) -+ result = sepgsqlCheckDatabaseSuperuser(); ++ result = sepgsql_database_superuser(MyDatabaseId); + return result; } @@ -14158,6 +17379,27 @@ diff -Nrpc base/src/include/access/tupdesc.h sepgsql/src/include/access/tupdesc. int tdrefcount; /* reference count, or -1 if not counting */ } *TupleDesc; +diff -Nrpc base/src/include/bootstrap/bootstrap.h sepgsql/src/include/bootstrap/bootstrap.h +*** base/src/include/bootstrap/bootstrap.h Sat Jan 3 12:25:21 2009 +--- sepgsql/src/include/bootstrap/bootstrap.h Tue Dec 8 14:04:25 2009 +*************** typedef enum +*** 70,76 **** + BootstrapProcess, + StartupProcess, + BgWriterProcess, +! WalWriterProcess + } AuxProcType; + + #endif /* BOOTSTRAP_H */ +--- 70,77 ---- + BootstrapProcess, + StartupProcess, + BgWriterProcess, +! WalWriterProcess, +! SelinuxReceiverProcess, + } AuxProcType; + + #endif /* BOOTSTRAP_H */ diff -Nrpc base/src/include/catalog/dependency.h sepgsql/src/include/catalog/dependency.h *** base/src/include/catalog/dependency.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/catalog/dependency.h Thu Jul 16 17:22:29 2009 @@ -14294,6 +17536,26 @@ diff -Nrpc base/src/include/catalog/pg_attribute.h sepgsql/src/include/catalog/p /* ---------------- * pg_index +diff -Nrpc base/src/include/catalog/pg_conversion_fn.h sepgsql/src/include/catalog/pg_conversion_fn.h +*** base/src/include/catalog/pg_conversion_fn.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/catalog/pg_conversion_fn.h Thu Sep 17 22:10:19 2009 +*************** +*** 17,23 **** + extern Oid ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +! Oid conproc, bool def); + extern void RemoveConversionById(Oid conversionOid); + extern Oid FindConversion(const char *conname, Oid connamespace); + extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); +--- 17,23 ---- + extern Oid ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +! Oid conproc, Oid consecid, bool def); + extern void RemoveConversionById(Oid conversionOid); + extern Oid FindConversion(const char *conname, Oid connamespace); + extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); diff -Nrpc base/src/include/catalog/pg_namespace.h sepgsql/src/include/catalog/pg_namespace.h *** base/src/include/catalog/pg_namespace.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/catalog/pg_namespace.h Wed Jul 15 19:35:52 2009 @@ -14833,10 +18095,10 @@ diff -Nrpc base/src/include/security/rowlevel.h sepgsql/src/include/security/row + #endif /* ROWLEVEL_H */ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepgsql.h *** base/src/include/security/sepgsql.h Thu Jan 1 09:00:00 1970 ---- sepgsql/src/include/security/sepgsql.h Thu Sep 10 15:18:03 2009 +--- sepgsql/src/include/security/sepgsql.h Tue Dec 8 14:04:25 2009 *************** *** 0 **** ---- 1,534 ---- +--- 1,777 ---- + /* + * src/include/security/sepgsql.h + * Headers of SE-PostgreSQL @@ -14881,7 +18143,6 @@ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepg + SEPG_CLASS_FIFO_FILE, + SEPG_CLASS_DB_DATABASE, + SEPG_CLASS_DB_SCHEMA, -+ SEPG_CLASS_DB_SCHEMA_TEMP, + SEPG_CLASS_DB_TABLE, + SEPG_CLASS_DB_SEQUENCE, + SEPG_CLASS_DB_PROCEDURE, @@ -14895,24 +18156,38 @@ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepg + + #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) @@ -14935,16 +18210,6 @@ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepg + #define SEPG_DB_SCHEMA__ADD_NAME (1<<7) + #define SEPG_DB_SCHEMA__REMOVE_NAME (1<<8) + -+ #define SEPG_DB_SCHEMA_TEMP__CREATE (SEPG_DB_DATABASE__CREATE) -+ #define SEPG_DB_SCHEMA_TEMP__DROP (SEPG_DB_DATABASE__DROP) -+ #define SEPG_DB_SCHEMA_TEMP__GETATTR (SEPG_DB_DATABASE__GETATTR) -+ #define SEPG_DB_SCHEMA_TEMP__SETATTR (SEPG_DB_DATABASE__SETATTR) -+ #define SEPG_DB_SCHEMA_TEMP__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+ #define SEPG_DB_SCHEMA_TEMP__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+ #define SEPG_DB_SCHEMA_TEMP__SEARCH (SEPG_DB_SCHEMA__SEARCH) -+ #define SEPG_DB_SCHEMA_TEMP__ADD_NAME (SEPG_DB_SCHEMA__ADD_NAME) -+ #define SEPG_DB_SCHEMA_TEMP__REMOVE_NAME (SEPG_DB_SCHEMA__REMOVE_NAME) -+ + #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) @@ -15060,7 +18325,275 @@ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepg + security_context_t tcontext, + uint16 tclass); + -+ extern pid_t sepgsqlStartupWorkerProcess(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, bool check_rights); ++ extern void ++ sepgsql_sequence_get_value(Oid seqOid); ++ extern void ++ sepgsql_sequence_next_value(Oid seqOid); ++ extern void ++ sepgsql_sequence_set_value(Oid seqOid); ++ ++ /* pg_conversion */ ++ extern Oid ++ sepgsql_conversion_create(const char *convName, Oid nspOid, Oid procOid); ++ extern void ++ sepgsql_conversion_alter(Oid convOid, const char *newName); ++ extern void ++ sepgsql_conversion_drop(Oid convOid); ++ ++ /* pg_database */ ++ extern Oid ++ sepgsql_database_create(const char *datName, DefElem *newLabel); ++ extern void ++ sepgsql_database_alter(Oid datOid); ++ extern void ++ sepgsql_database_drop(Oid datOid); ++ extern Oid ++ sepgsql_database_relabel(Oid datOid, DefElem *newLabel); ++ extern void ++ sepgsql_database_grant(Oid datOid); ++ extern void ++ sepgsql_database_access(Oid datOid); ++ extern bool ++ sepgsql_database_superuser(Oid datOid); ++ extern void ++ sepgsql_database_load_module(Oid datOid, const char *filename); ++ ++ /* pg_foreign_data_wrapper */ ++ extern Oid ++ sepgsql_fdw_create(const char *fdwName, Oid fdwValidator); ++ extern void ++ sepgsql_fdw_alter(Oid fdwOid, Oid newValidator); ++ extern void ++ sepgsql_fdw_drop(Oid fdwOid); ++ extern void ++ sepgsql_fdw_grant(Oid fdwOid); ++ ++ /* pg_foreign_server */ ++ extern Oid ++ sepgsql_foreign_server_create(const char *fsrvName); ++ extern void ++ sepgsql_foreign_server_alter(Oid fsrvOid); ++ extern void ++ sepgsql_foreign_server_drop(Oid fsrvOid); ++ extern void ++ sepgsql_foreign_server_grant(Oid fsrvOid); ++ ++ /* pg_language */ ++ extern Oid ++ sepgsql_language_create(const char *langName, Oid handlerOid, Oid validatorOid); ++ extern void ++ sepgsql_language_alter(Oid langOid); ++ extern void ++ sepgsql_language_drop(Oid langOid); ++ extern void ++ sepgsql_language_grant(Oid langOid); ++ ++ /* pg_largeobject */ ++ extern Oid ++ sepgsql_largeobject_create(Oid loid, DefElem *newLabel); ++ extern void ++ sepgsql_largeobject_alter(Oid loid, Oid newOwner); ++ extern void ++ sepgsql_largeobject_drop(Oid loid, bool dacSkip); ++ extern void ++ sepgsql_largeobject_read(Oid loid); ++ extern void ++ sepgsql_largeobject_write(Oid loid); ++ extern void ++ sepgsql_largeobject_export(Oid loid, const char *filename); ++ extern Oid ++ sepgsql_largeobject_import(Oid loid, const char *filename, DefElem *newLabel); ++ ++ /* pg_namespace */ ++ extern Oid ++ sepgsql_schema_create(const char *nspName, bool isTemp, DefElem *newLabel); ++ extern void ++ sepgsql_schema_alter(Oid nspOid); ++ extern void ++ sepgsql_schema_drop(Oid nspOid); ++ extern Oid ++ sepgsql_schema_relabel(Oid nspOid, DefElem *newLabel); ++ extern void ++ sepgsql_schema_grant(Oid nspOid); ++ extern bool ++ sepgsql_schema_search(Oid nspOid, bool abort); ++ ++ /* pg_opclass */ ++ extern Oid ++ sepgsql_opclass_create(const char *opcName, Oid nspOid); ++ extern void ++ sepgsql_opclass_alter(Oid opcOid, const char *newName); ++ extern void ++ sepgsql_opclass_drop(Oid opcOid); ++ ++ /* pg_opfamily */ ++ extern Oid ++ sepgsql_opfamily_create(const char *opfName, Oid nspOid); ++ extern void ++ sepgsql_opfamily_alter(Oid opfOid, const char *newName); ++ extern void ++ sepgsql_opfamily_drop(Oid opfOid); ++ extern void ++ sepgsql_opfamily_add_operator(Oid opfOid, Oid operOid); ++ extern void ++ sepgsql_opfamily_add_procedure(Oid opfOid, Oid procOid); ++ ++ /* pg_operator */ ++ extern Oid ++ sepgsql_operator_create(const char *oprName, Oid oprOid, Oid nspOid, ++ Oid codeFn, Oid restFn, Oid joinFn); ++ extern void ++ sepgsql_operator_alter(Oid oprOid); ++ extern void ++ sepgsql_operator_drop(Oid oprOid); ++ ++ /* pg_proc */ ++ extern Oid ++ sepgsql_proc_create(const char *procName, HeapTuple oldTup, ++ Oid nspOid, Oid langOid, DefElem *newLabel); ++ extern void ++ sepgsql_proc_alter(Oid procOid, const char *newName, Oid newNsp); ++ extern void ++ sepgsql_proc_drop(Oid procOid); ++ extern Oid ++ sepgsql_proc_relabel(Oid procOid, DefElem *newLabel); ++ extern void ++ sepgsql_proc_grant(Oid procOid); ++ extern void ++ sepgsql_proc_execute(Oid procOid); ++ extern bool ++ sepgsql_proc_hint_inlined(HeapTuple protup); ++ extern void ++ sepgsql_proc_entrypoint(FmgrInfo *flinfo, HeapTuple protup); ++ ++ /* pg_rewrite */ ++ extern void ++ sepgsql_rule_create(Oid relOid, const char *ruleName); ++ extern void ++ sepgsql_rule_drop(Oid relOid, const char *ruleName); ++ ++ /* pg_trigger */ ++ extern void ++ sepgsql_trigger_create(Oid relOid, const char *trigName, Oid procOid); ++ extern void ++ sepgsql_trigger_alter(Oid relOid, const char *trigName); ++ extern void ++ sepgsql_trigger_drop(Oid relOid, const char *trigName); ++ ++ /* pg_ts_config */ ++ extern Oid ++ sepgsql_ts_config_create(const char *cfgName, Oid nspOid); ++ extern void ++ sepgsql_ts_config_alter(Oid cfgOid, const char *newName); ++ extern void ++ sepgsql_ts_config_drop(Oid cfgOid); ++ ++ /* pg_ts_dict */ ++ extern Oid ++ sepgsql_ts_dict_create(const char *dictName, Oid nspOid); ++ extern void ++ sepgsql_ts_dict_alter(Oid dictOid, const char *newName); ++ extern void ++ sepgsql_ts_dict_drop(Oid dictOid); ++ ++ /* pg_ts_parser */ ++ extern Oid ++ sepgsql_ts_parser_create(const char *prsName, Oid nspOid, ++ Oid startFn, Oid tokenFn, Oid sendFn, ++ Oid headlineFn, Oid lextypeFn); ++ extern void ++ sepgsql_ts_parser_alter(Oid prsOid, const char *newName); ++ extern void ++ sepgsql_ts_parser_drop(Oid prsOid); ++ ++ /* pg_ts_templace */ ++ extern Oid ++ sepgsql_ts_template_create(const char *tmplName, Oid nspOid, ++ Oid initFn, Oid lexizeFn); ++ extern void ++ sepgsql_ts_template_alter(Oid tmplOid, const char *newName); ++ extern void ++ sepgsql_ts_template_drop(Oid tmplOid); ++ ++ /* pg_type */ ++ extern Oid ++ sepgsql_type_create(const char *typName, HeapTuple oldTup, Oid nspOid, ++ Oid inputProc, Oid outputProc, Oid recvProc, Oid sendProc, ++ Oid modinProc, Oid modoutProc, Oid analyzeProc); ++ extern void ++ sepgsql_type_alter(Oid typOid, const char *newName, Oid newNsp); ++ extern void ++ sepgsql_type_drop(Oid typOid); ++ ++ /* misc objects */ ++ extern void ++ sepgsql_sysobj_drop(const ObjectAddress *object); ++ ++ /* filesystem objects */ ++ void ++ sepgsql_file_stat(const char *filename); ++ void ++ sepgsql_file_read(const char *filename); ++ void ++ sepgsql_file_write(const char *filename); + + /* + * checker.c : check permission on given queries @@ -15107,83 +18640,6 @@ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepg + /* + * hooks.c : routines to check certain permissions + */ -+ extern Oid -+ sepgsqlCheckDatabaseCreate(const char *datname, DefElem *newLabel); -+ extern void -+ sepgsqlCheckDatabaseDrop(Oid datOid); -+ extern void -+ sepgsqlCheckDatabaseSetattr(Oid datOid); -+ extern Oid -+ sepgsqlCheckDatabaseRelabel(Oid datOid, DefElem *newLlabel); -+ extern void -+ sepgsqlCheckDatabaseAccess(Oid datOid); -+ extern bool -+ sepgsqlCheckDatabaseSuperuser(void); -+ extern void -+ sepgsqlCheckDatabaseLoadModule(const char *filename); -+ -+ extern Oid -+ sepgsqlCheckSchemaCreate(const char *nspName, DefElem *new_label, bool isTemp); -+ extern void -+ sepgsqlCheckSchemaDrop(Oid nspOid); -+ extern void -+ sepgsqlCheckSchemaSetattr(Oid nspOid); -+ extern Oid -+ sepgsqlCheckSchemaRelabel(Oid nspOid, DefElem *new_label); -+ extern void -+ sepgsqlCheckSchemaAddName(Oid nspOid); -+ extern void -+ sepgsqlCheckSchemaRemoveName(Oid nspOid); -+ extern bool -+ sepgsqlCheckSchemaSearch(Oid nspOid, bool abort); -+ -+ extern void -+ sepgsqlCheckTableDrop(Oid table_oid); -+ extern void -+ sepgsqlCheckTableSetattr(Oid table_oid); -+ extern Oid -+ sepgsqlCheckTableRelabel(Oid table_oid, DefElem *new_label); -+ extern void -+ sepgsqlCheckTableLock(Oid table_oid); -+ extern void -+ sepgsqlCheckTableTruncate(Relation rel); -+ extern void -+ sepgsqlCheckTableReference(Relation rel, int16 *attnums, int natts); -+ -+ extern void -+ sepgsqlCheckSequenceGetValue(Oid seqOid); -+ extern void -+ sepgsqlCheckSequenceNextValue(Oid seqOid); -+ extern void -+ sepgsqlCheckSequenceSetValue(Oid seqOid); -+ -+ extern Oid -+ sepgsqlCheckColumnCreate(Oid relOid, const char *attname, DefElem *newLabel); -+ extern void -+ sepgsqlCheckColumnDrop(Oid relOid, AttrNumber attno); -+ extern void -+ sepgsqlCheckColumnSetattr(Oid relOid, AttrNumber attno); -+ extern Oid -+ sepgsqlCheckColumnRelabel(Oid relOid, AttrNumber attno, DefElem *newLabel); -+ -+ extern Oid -+ sepgsqlCheckProcedureCreate(const char *procName, Oid procOid, -+ Oid procNsp, Oid procLang, DefElem *newLabel); -+ extern void -+ sepgsqlCheckProcedureDrop(Oid procOid); -+ extern void -+ sepgsqlCheckProcedureSetattr(Oid procOid); -+ extern Oid -+ sepgsqlCheckProcedureRelabel(Oid procOid, DefElem *newLabel); -+ extern void -+ sepgsqlCheckProcedureExecute(Oid procOid); -+ extern void -+ sepgsqlCheckProcedureInstall(Oid procOid); -+ extern bool -+ sepgsqlHintProcedureInlined(HeapTuple protup); -+ extern void -+ sepgsqlCheckProcedureEntrypoint(FmgrInfo *flinfo, HeapTuple protup); -+ + extern void + sepgsqlCheckBlobCreate(Relation rel, HeapTuple lotup); + extern void @@ -15197,28 +18653,12 @@ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepg + extern void + sepgsqlCheckBlobSetattr(HeapTuple tuple); + extern void -+ sepgsqlCheckBlobExport(LargeObjectDesc *lobj, -+ int fdesc, const char *filename); ++ sepgsqlCheckBlobExport(LargeObjectDesc *lobj, const char *filename); + extern void -+ sepgsqlCheckBlobImport(LargeObjectDesc *lobj, -+ int fdesc, const char *filename); ++ sepgsqlCheckBlobImport(LargeObjectDesc *lobj, const char *filename); + extern void + sepgsqlCheckBlobRelabel(HeapTuple oldtup, HeapTuple newtup); + -+ extern void -+ sepgsqlCheckFileRead(int fdesc, const char *filename); -+ extern void -+ sepgsqlCheckFileWrite(int fdesc, const char *filename); -+ -+ extern Oid -+ sepgsqlCheckSysobjCreate(Oid relid, const char *auditName); -+ extern void -+ sepgsqlCheckSysobjGetattr(Oid relid, Oid secid, const char *auditName); -+ extern void -+ sepgsqlCheckSysobjSetattr(Oid relid, Oid secid, const char *auditName); -+ extern void -+ sepgsqlCheckSysobjDrop(const ObjectAddress *object); -+ + /* + * label.c : security label management + */ @@ -15242,9 +18682,9 @@ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepg + extern Oid *sepgsqlCopyTableColumns(Relation source); + + extern sepgsql_sid_t -+ sepgsqlGetTupleContext(Oid tableOid, HeapTuple tuple, uint16 *tclass); ++ sepgsqlGetTupleSecid(Oid tableOid, HeapTuple tuple, uint16 *tclass); + extern sepgsql_sid_t -+ sepgsqlGetSysobjContext(Oid tableOid, Oid objectId, int32 objsubId, uint16 *tclass); ++ sepgsqlGetSysobjSecid(Oid tableOid, Oid objectId, int32 objsubId, uint16 *tclass); + + extern char *sepgsqlTransSecLabelIn(char *seclabel); + extern char *sepgsqlTransSecLabelOut(char *seclabel); @@ -15255,13 +18695,11 @@ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepg + /* + * perms.c : SELinux permission related stuff + */ -+ extern const char *sepgsqlAuditName(Oid relid, HeapTuple tuple); ++ extern uint16 sepgsqlFileObjectClass(int fdesc); + -+ extern security_class_t sepgsqlFileObjectClass(int fdesc); ++ extern uint16 sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple); + -+ extern security_class_t sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple); -+ -+ extern security_class_t sepgsqlTransToExternalClass(security_class_t tclass_in); ++ extern security_class_t sepgsqlTransToExternalClass(uint16 tclass_in); + + extern void sepgsqlTransToInternalPerms(security_class_t tclass_ex, + struct av_decision *avd); @@ -15272,7 +18710,6 @@ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepg + + /* avc.c */ + #define sepgsqlShmemSize() (0) -+ #define sepgsqlStartupWorkerProcess() (0) + + /* checker.c */ + #define sepgsqlCheckRTEPerms(a) do {} while(0) @@ -15287,71 +18724,139 @@ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepg + #define sepgsqlIsEnabled() (false) + #define sepgsqlInitialize() do {} while(0) + -+ /* hooks.c */ -+ #define sepgsqlCheckDatabaseCreate(a,b) (InvalidOid) -+ #define sepgsqlCheckDatabaseDrop(a) do {} while(0) -+ #define sepgsqlCheckDatabaseSetattr(a) do {} while(0) -+ #define sepgsqlCheckDatabaseRelabel(a,b) (InvalidOid) -+ #define sepgsqlCheckDatabaseAccess(a) (true) -+ #define sepgsqlCheckDatabaseSuperuser() (true) -+ #define sepgsqlCheckDatabaseLoadModule(a) 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 sepgsqlCheckSchemaCreate(a,b,c) (InvalidOid) -+ #define sepgsqlCheckSchemaDrop(a) do {} while(0) -+ #define sepgsqlCheckSchemaSetattr(a) do {} while(0) -+ #define sepgsqlCheckSchemaRelabel(a,b) (InvalidOid) -+ #define sepgsqlCheckSchemaAddName(a) do {} while(0) -+ #define sepgsqlCheckSchemaRemoveName(a) do {} while(0) -+ #define sepgsqlCheckSchemaSearch(a,b) (true) ++ #define sepgsql_cast_create(a,b,c) (InvalidOid) ++ #define sepgsql_cast_drop(a) (InvalidOid) + -+ #define sepgsqlCheckTableDrop(a) do {} while(0) -+ #define sepgsqlCheckTableSetattr(a) do {} while(0) -+ #define sepgsqlCheckTableRelabel(a,b) (InvalidOid) -+ #define sepgsqlCheckTableLock(a) do {} while(0) -+ #define sepgsqlCheckTableTruncate(a) do {} while(0) -+ #define sepgsqlCheckTableReference(a,b,c) do {} while(0) ++ #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 sepgsqlCheckSequenceGetValue(a) do {} while(0) -+ #define sepgsqlCheckSequenceNextValue(a) do {} while(0) -+ #define sepgsqlCheckSequenceSetValue(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 sepgsqlCheckColumnCreate(a,b,c) (InvalidOid) -+ #define sepgsqlCheckColumnDrop(a,b) do {} while(0) -+ #define sepgsqlCheckColumnSetattr(a,b) do {} while(0) -+ #define sepgsqlCheckColumnRelabel(a,b,c) (InvalidOid) ++ #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 sepgsqlCheckProcedureCreate(a,b,c,d) (InvalidOid) -+ #define sepgsqlCheckProcedureDrop(a) do {} while(0) -+ #define sepgsqlCheckProcedureSetattr(a) do {} while(0) -+ #define sepgsqlCheckProcedureRelabel(a,b) (InvalidOid) -+ #define sepgsqlCheckProcedureExecute(a) (true) -+ #define sepgsqlCheckProcedureInstall(a) do {} while(0) -+ #define sepgsqlHintProcedureInlined(a) (true) -+ #define sepgsqlCheckProcedureEntrypoint(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 sepgsqlCheckBlobCreate(a,b) do {} while(0) -+ #define sepgsqlCheckBlobDrop(a,b) do {} while(0) -+ #define sepgsqlCheckBlobRead(a) do {} while(0) -+ #define sepgsqlCheckBlobWrite(a) do {} while(0) -+ #define sepgsqlCheckBlobGetattr(a) do {} while(0) -+ #define sepgsqlCheckBlobSetattr(a) do {} while(0) -+ #define sepgsqlCheckBlobExport(a,b,c) do {} while(0) -+ #define sepgsqlCheckBlobImport(a,b,c) do {} while(0) -+ #define sepgsqlCheckBlobRelabel(a,b) do {} while(0) -+ #define sepgsqlCheckFileRead(a,b) do {} while(0) -+ #define sepgsqlCheckFileWrite(a,b) 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 sepgsqlCheckSysobjCreate(a,b) (InvalidOid) -+ #define sepgsqlCheckSysobjGetattr(a,b,c) do {} while(0) -+ #define sepgsqlCheckSysobjSetattr(a,b,c) do {} while(0) -+ #define sepgsqlCheckSysobjDrop(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 sepgsqlCreateTableColumns(a,b,c,d,e) (NULL) -+ #define sepgsqlCopyTableColumns(a) (NULL) -+ #define sepgsqlMetaSecurityLabel() (NULL) + #define sepgsqlTransSecLabelIn(a) (a) + #define sepgsqlTransSecLabelOut(a) (a) + #define sepgsqlRawSecLabelIn(a) (a) @@ -15420,6 +18925,30 @@ diff -Nrpc base/src/include/storage/lwlock.h sepgsql/src/include/storage/lwlock. /* Individual lock IDs end here */ FirstBufMappingLock, FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, +diff -Nrpc base/src/include/storage/proc.h sepgsql/src/include/storage/proc.h +*** base/src/include/storage/proc.h Thu Feb 26 10:18:55 2009 +--- sepgsql/src/include/storage/proc.h Tue Dec 8 14:04:25 2009 +*************** typedef struct PROC_HDR +*** 143,150 **** + * normal operation. Startup process also consumes one slot, but WAL + * writer and autovacuum launcher are launched only after it has + * exited. + */ +! #define NUM_AUXILIARY_PROCS 3 + + + /* configurable options */ +--- 143,152 ---- + * normal operation. Startup process also consumes one slot, but WAL + * writer and autovacuum launcher are launched only after it has + * exited. ++ * In addition, a netlink receiver process may be launched, if SELinux ++ * support is enabled. + */ +! #define NUM_AUXILIARY_PROCS 4 + + + /* configurable options */ diff -Nrpc base/src/include/utils/errcodes.h sepgsql/src/include/utils/errcodes.h *** base/src/include/utils/errcodes.h Fri Mar 6 09:45:33 2009 --- sepgsql/src/include/utils/errcodes.h Wed Jul 15 19:35:52 2009 @@ -15451,6 +18980,41 @@ diff -Nrpc base/src/include/utils/syscache.h sepgsql/src/include/utils/syscache. STATRELATT, TSCONFIGMAP, TSCONFIGNAMENSP, +diff -Nrpc base/src/test/regress/GNUmakefile sepgsql/src/test/regress/GNUmakefile +*** base/src/test/regress/GNUmakefile Sat Jan 3 13:01:35 2009 +--- sepgsql/src/test/regress/GNUmakefile Tue Dec 1 17:11:40 2009 +*************** ifdef NO_LOCALE +*** 38,43 **** +--- 38,49 ---- + NOLOCALE += --no-locale + endif + ++ # SELinux support ++ ENABLE_SELINUX = ++ ifdef SELINUX ++ ENABLE_SELINUX += --enable-selinux ++ endif ++ + # stuff to pass into build of pg_regress + EXTRADEFS = '-DHOST_TUPLE="$(host_tuple)"' \ + '-DMAKEPROG="$(MAKE)"' \ +*************** tablespace-setup: +*** 138,144 **** + ## Run tests + ## + +! pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) + + check: all + $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) +--- 144,150 ---- + ## Run tests + ## + +! pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) $(ENABLE_SELINUX) + + check: all + $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) diff -Nrpc base/src/test/regress/expected/sanity_check.out sepgsql/src/test/regress/expected/sanity_check.out *** base/src/test/regress/expected/sanity_check.out Tue Feb 10 10:10:02 2009 --- sepgsql/src/test/regress/expected/sanity_check.out Thu Jul 16 23:18:11 2009 @@ -15481,3 +19045,72 @@ diff -Nrpc base/src/test/regress/expected/sanity_check.out sepgsql/src/test/regr -- -- another sanity check: every system catalog that has OIDs should have +diff -Nrpc base/src/test/regress/pg_regress.c sepgsql/src/test/regress/pg_regress.c +*** base/src/test/regress/pg_regress.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/test/regress/pg_regress.c Tue Dec 1 17:11:40 2009 +*************** static _stringlist *schedulelist = NULL; +*** 82,87 **** +--- 82,88 ---- + static _stringlist *extra_tests = NULL; + static char *temp_install = NULL; + static char *temp_config = NULL; ++ static bool enable_selinux = false; + static char *top_builddir = NULL; + static bool nolocale = false; + static char *hostname = NULL; +*************** help(void) +*** 1863,1868 **** +--- 1864,1870 ---- + printf(_(" --top-builddir=DIR (relative) path to top level build directory\n")); + printf(_(" --port=PORT start postmaster on PORT\n")); + printf(_(" --temp-config=PATH append contents of PATH to temporary config\n")); ++ printf(_(" --enable-selinux enables SELinux support, if available\n")); + printf(_("\n")); + printf(_("Options for using an existing installation:\n")); + printf(_(" --host=HOST use postmaster running on HOST\n")); +*************** regression_main(int argc, char *argv[], +*** 1907,1912 **** +--- 1909,1915 ---- + {"dlpath", required_argument, NULL, 17}, + {"create-role", required_argument, NULL, 18}, + {"temp-config", required_argument, NULL, 19}, ++ {"enable-selinux", optional_argument, NULL, 20}, + {NULL, 0, NULL, 0} + }; + +*************** regression_main(int argc, char *argv[], +*** 1997,2002 **** +--- 2000,2008 ---- + case 19: + temp_config = strdup(optarg); + break; ++ case 20: ++ enable_selinux = true; ++ break; + default: + /* getopt_long already emitted a complaint */ + fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"), +*************** regression_main(int argc, char *argv[], +*** 2086,2095 **** + /* initdb */ + header(_("initializing database system")); + snprintf(buf, sizeof(buf), +! SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE, + bindir, temp_install, datadir, + debug ? " --debug" : "", + nolocale ? " --no-locale" : "", + outputdir); + if (system(buf)) + { +--- 2092,2102 ---- + /* initdb */ + header(_("initializing database system")); + snprintf(buf, sizeof(buf), +! SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE, + bindir, temp_install, datadir, + debug ? " --debug" : "", + nolocale ? " --no-locale" : "", ++ enable_selinux ? " --enable-selinux" : "", + outputdir); + if (system(buf)) + { diff --git a/sepostgresql.spec b/sepostgresql.spec index 49d6727..1c85cb6 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -12,7 +12,7 @@ Summary: Security Enhanced PostgreSQL Name: sepostgresql Version: 8.4.1 -Release: 2305%{?dist} +Release: 2464%{?dist} License: BSD Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ @@ -192,6 +192,10 @@ fi %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog +* Fri Dec 8 2009 KaiGai Kohei - 8.4.1-2464 +- rework: backport features from v8.5devel tree +- fixbug: selinux netlink receiver process didn't have correct ps display + * Fri Sep 11 2009 KaiGai Kohei - 8.4.1-2305 - Upgrade base SE-PostgreSQL v8.4.0->v8.4.1 - rework: backport features from v8.5devel tree From 374d1c5e6e886b1ebec1a93ff332e3aaa1924f98 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Wed, 16 Dec 2009 13:54:47 +0000 Subject: [PATCH 03/24] upgrade base version 8.4.1->8.4.2 --- .cvsignore | 2 +- sepostgresql-8.4.patch | 493 +++++++++++++++++++++-------------------- sepostgresql.spec | 7 +- sources | 2 +- 4 files changed, 254 insertions(+), 250 deletions(-) diff --git a/.cvsignore b/.cvsignore index 19279bd..227e30f 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1 @@ -postgresql-8.4.1.tar.bz2 +postgresql-8.4.2.tar.bz2 diff --git a/sepostgresql-8.4.patch b/sepostgresql-8.4.patch index b703db3..0a1331d 100644 --- a/sepostgresql-8.4.patch +++ b/sepostgresql-8.4.patch @@ -1,6 +1,6 @@ diff -Nrpc base/configure sepgsql/configure -*** base/configure Sun Sep 6 19:40:49 2009 ---- sepgsql/configure Sun Sep 6 19:53:10 2009 +*** base/configure Tue Dec 15 17:16:51 2009 +--- sepgsql/configure Tue Dec 15 17:30:25 2009 *************** with_libxml *** 710,715 **** --- 710,716 ---- @@ -189,8 +189,8 @@ diff -Nrpc base/configure sepgsql/configure elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 diff -Nrpc base/configure.in sepgsql/configure.in -*** base/configure.in Sun Sep 6 19:40:49 2009 ---- sepgsql/configure.in Sun Sep 6 19:53:10 2009 +*** base/configure.in Tue Dec 15 17:16:51 2009 +--- sepgsql/configure.in Tue Dec 15 17:30:25 2009 *************** PGAC_ARG_BOOL(with, zlib, yes, *** 764,769 **** --- 764,782 ---- @@ -227,8 +227,8 @@ diff -Nrpc base/src/Makefile.global.in sepgsql/src/Makefile.global.in enable_thread_safety = @enable_thread_safety@ diff -Nrpc base/src/backend/Makefile sepgsql/src/backend/Makefile -*** base/src/backend/Makefile Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/Makefile Wed Jul 15 19:35:52 2009 +*** base/src/backend/Makefile Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/Makefile Tue Dec 15 17:30:25 2009 *************** include $(top_builddir)/src/Makefile.glo *** 16,22 **** @@ -247,8 +247,8 @@ diff -Nrpc base/src/backend/Makefile sepgsql/src/backend/Makefile include $(srcdir)/common.mk *************** LIBS := $(filter-out -lpgport, $(LIBS)) -*** 34,39 **** ---- 34,44 ---- +*** 40,45 **** +--- 40,50 ---- # The backend doesn't need everything that's in LIBS, however LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) @@ -498,8 +498,8 @@ diff -Nrpc base/src/backend/access/heap/tuptoaster.c sepgsql/src/backend/access/ /* diff -Nrpc base/src/backend/access/transam/xact.c sepgsql/src/backend/access/transam/xact.c -*** base/src/backend/access/transam/xact.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/access/transam/xact.c Sun Sep 6 19:31:39 2009 +*** base/src/backend/access/transam/xact.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/access/transam/xact.c Tue Dec 15 17:30:25 2009 *************** *** 36,41 **** --- 36,43 ---- @@ -515,7 +515,7 @@ diff -Nrpc base/src/backend/access/transam/xact.c sepgsql/src/backend/access/tra *** 140,145 **** --- 142,148 ---- Oid prevUser; /* previous CurrentUserId setting */ - bool prevSecDefCxt; /* previous SecurityDefinerContext setting */ + int prevSecContext; /* previous SecurityRestrictionContext */ bool prevXactReadOnly; /* entry-time xact r/o state */ + int prevRowlv; /* previous Row-level control behavior */ struct TransactionStateData *parent; /* back link to parent */ @@ -525,7 +525,7 @@ diff -Nrpc base/src/backend/access/transam/xact.c sepgsql/src/backend/access/tra *** 168,173 **** --- 171,177 ---- InvalidOid, /* previous CurrentUserId setting */ - false, /* previous SecurityDefinerContext setting */ + 0, /* previous SecurityRestrictionContext */ false, /* entry-time xact r/o state */ + ROWLV_FILTER_MODE, /* previous Row-level control behavior */ NULL /* link to parent state block */ @@ -536,15 +536,15 @@ diff -Nrpc base/src/backend/access/transam/xact.c sepgsql/src/backend/access/tra --- 1528,1534 ---- s->nChildXids = 0; s->maxChildXids = 0; - GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt); + GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); + s->prevRowlv = rowlvGetPerformingMode(); - /* SecurityDefinerContext should never be set outside a transaction */ - Assert(!s->prevSecDefCxt); + /* SecurityRestrictionContext should never be set outside a transaction */ + Assert(s->prevSecContext == 0); *************** AbortTransaction(void) -*** 2030,2035 **** ---- 2035,2050 ---- - SetUserIdAndContext(s->prevUser, s->prevSecDefCxt); +*** 2031,2036 **** +--- 2036,2051 ---- + SetUserIdAndSecContext(s->prevUser, s->prevSecContext); /* + * Reset behavior of row-level access controls @@ -561,9 +561,9 @@ diff -Nrpc base/src/backend/access/transam/xact.c sepgsql/src/backend/access/tra */ AfterTriggerEndXact(false); *************** AbortSubTransaction(void) -*** 3873,3878 **** ---- 3888,3903 ---- - SetUserIdAndContext(s->prevUser, s->prevSecDefCxt); +*** 3874,3879 **** +--- 3889,3904 ---- + SetUserIdAndSecContext(s->prevUser, s->prevSecContext); /* + * Reset behavior of row-level access controls @@ -580,10 +580,10 @@ diff -Nrpc base/src/backend/access/transam/xact.c sepgsql/src/backend/access/tra * ResourceOwner... */ *************** PushTransaction(void) -*** 4014,4019 **** ---- 4039,4045 ---- +*** 4015,4020 **** +--- 4040,4046 ---- s->blockState = TBLOCK_SUBBEGIN; - GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt); + GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); s->prevXactReadOnly = XactReadOnly; + s->prevRowlv = rowlvGetPerformingMode(); @@ -920,8 +920,8 @@ diff -Nrpc base/src/backend/catalog/catalog.c sepgsql/src/backend/catalog/catalo relationId == PgShdescriptionToastIndex) return true; diff -Nrpc base/src/backend/catalog/dependency.c sepgsql/src/backend/catalog/dependency.c -*** base/src/backend/catalog/dependency.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/catalog/dependency.c Thu Sep 17 23:44:07 2009 +*** base/src/backend/catalog/dependency.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/catalog/dependency.c Tue Dec 15 17:30:25 2009 *************** *** 63,68 **** --- 63,69 ---- @@ -1045,7 +1045,7 @@ diff -Nrpc base/src/backend/catalog/dependency.c sepgsql/src/backend/catalog/dep /* And clean up */ *************** reportDependentObjects(const ObjectAddre -*** 942,954 **** +*** 943,955 **** * depRel is the already-open pg_depend relation. */ static void @@ -1059,7 +1059,7 @@ diff -Nrpc base/src/backend/catalog/dependency.c sepgsql/src/backend/catalog/dep /* * First remove any pg_depend records that link from this object to * others. (Any records linking to this object should be gone already.) ---- 957,973 ---- +--- 958,974 ---- * depRel is the already-open pg_depend relation. */ static void @@ -1435,8 +1435,8 @@ diff -Nrpc base/src/backend/catalog/heap.c sepgsql/src/backend/catalog/heap.c diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c -*** base/src/backend/catalog/index.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/index.c Fri Sep 18 17:02:48 2009 +*** base/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 ---- @@ -1448,7 +1448,7 @@ diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c #include "storage/lmgr.h" #include "storage/procarray.h" *************** AppendAttributeTuples(Relation indexRela -*** 351,357 **** +*** 352,358 **** Assert(indexTupDesc->attrs[i]->attnum == i + 1); Assert(indexTupDesc->attrs[i]->attcacheoff == -1); @@ -1456,7 +1456,7 @@ diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c } CatalogCloseIndexes(indstate); ---- 352,359 ---- +--- 353,360 ---- Assert(indexTupDesc->attrs[i]->attnum == i + 1); Assert(indexTupDesc->attrs[i]->attcacheoff == -1); @@ -1466,7 +1466,7 @@ diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c CatalogCloseIndexes(indstate); *************** index_create(Oid heapRelationId, -*** 652,658 **** +*** 653,659 **** */ InsertPgClassTuple(pg_class, indexRelation, RelationGetRelid(indexRelation), @@ -1474,7 +1474,7 @@ diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c /* done with pg_class */ heap_close(pg_class, RowExclusiveLock); ---- 654,660 ---- +--- 655,661 ---- */ InsertPgClassTuple(pg_class, indexRelation, RelationGetRelid(indexRelation), @@ -1861,8 +1861,8 @@ diff -Nrpc base/src/backend/catalog/pg_operator.c sepgsql/src/backend/catalog/pg operatorObjectId = simple_heap_insert(pg_operator_desc, tup); } diff -Nrpc base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_proc.c -*** base/src/backend/catalog/pg_proc.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/pg_proc.c Fri Sep 18 17:39:46 2009 +*** base/src/backend/catalog/pg_proc.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/catalog/pg_proc.c Tue Dec 15 17:30:25 2009 *************** *** 29,34 **** --- 29,35 ---- @@ -1892,8 +1892,8 @@ diff -Nrpc base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_pro Oid retval; int parameterCount; *************** ProcedureCreate(const char *procedureNam -*** 96,101 **** ---- 98,104 ---- +*** 97,102 **** +--- 99,105 ---- Datum values[Natts_pg_proc]; bool replaces[Natts_pg_proc]; Oid relid; @@ -1902,8 +1902,8 @@ diff -Nrpc base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_pro TupleDesc tupDesc; bool is_update; *************** ProcedureCreate(const char *procedureNam -*** 343,348 **** ---- 346,356 ---- +*** 344,349 **** +--- 347,357 ---- ObjectIdGetDatum(procNamespace), 0); @@ -1916,8 +1916,8 @@ diff -Nrpc base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_pro { /* There is one; okay to replace it? */ *************** ProcedureCreate(const char *procedureNam -*** 477,482 **** ---- 485,492 ---- +*** 481,486 **** +--- 489,496 ---- /* Okay, do it... */ tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); @@ -1927,8 +1927,8 @@ diff -Nrpc base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_pro ReleaseSysCache(oldtup); *************** ProcedureCreate(const char *procedureNam -*** 486,491 **** ---- 496,503 ---- +*** 490,495 **** +--- 500,507 ---- { /* Creating a new procedure */ tup = heap_form_tuple(tupDesc, values, nulls); @@ -4690,8 +4690,8 @@ diff -Nrpc base/src/backend/commands/proclang.c sepgsql/src/backend/commands/pro memset(repl_repl, false, sizeof(repl_repl)); diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/schemacmds.c -*** base/src/backend/commands/schemacmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/schemacmds.c Thu Sep 17 17:04:16 2009 +*** base/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 ---- @@ -4709,7 +4709,7 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s Oid owner_uid; Oid saved_uid; + Oid nspsecid; - bool saved_secdefcxt; + int save_sec_context; AclResult aclresult; *************** CreateSchemaCommand(CreateSchemaStmt *st @@ -4726,16 +4726,16 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s if (!allowSystemTableMods && IsReservedName(schemaName)) ereport(ERROR, *************** CreateSchemaCommand(CreateSchemaStmt *st -*** 94,100 **** - SetUserIdAndContext(owner_uid, true); +*** 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(); ---- 100,106 ---- - SetUserIdAndContext(owner_uid, true); +--- 101,107 ---- + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); /* Create the schema's namespace */ ! namespaceId = NamespaceCreate(schemaName, owner_uid, nspsecid); @@ -4743,7 +4743,7 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s /* Advance cmd counter to make the namespace visible */ CommandCounterIncrement(); *************** RenameSchema(const char *oldname, const -*** 267,274 **** +*** 268,275 **** errmsg("schema \"%s\" does not exist", oldname))); /* make sure the new name doesn't exist */ @@ -4752,7 +4752,7 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s CStringGetDatum(newname), 0, 0, 0))) ereport(ERROR, ---- 273,279 ---- +--- 274,280 ---- errmsg("schema \"%s\" does not exist", oldname))); /* make sure the new name doesn't exist */ @@ -4761,8 +4761,8 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s 0, 0, 0))) ereport(ERROR, *************** RenameSchema(const char *oldname, const -*** 286,291 **** ---- 291,299 ---- +*** 287,292 **** +--- 292,300 ---- aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); @@ -4773,8 +4773,8 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), *************** AlterSchemaOwner_internal(HeapTuple tup, -*** 397,402 **** ---- 405,413 ---- +*** 398,403 **** +--- 406,414 ---- aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); @@ -4785,8 +4785,8 @@ diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/s memset(repl_repl, false, sizeof(repl_repl)); *************** AlterSchemaOwner_internal(HeapTuple tup, -*** 431,433 **** ---- 442,492 ---- +*** 432,434 **** +--- 443,493 ---- } } @@ -4921,8 +4921,8 @@ diff -Nrpc base/src/backend/commands/sequence.c sepgsql/src/backend/commands/seq seq = read_info(elm, seqrel, &buf); diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/tablecmds.c -*** base/src/backend/commands/tablecmds.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/commands/tablecmds.c Thu Oct 8 09:29:32 2009 +*** base/src/backend/commands/tablecmds.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/commands/tablecmds.c Tue Dec 15 17:30:25 2009 *************** *** 62,67 **** --- 62,68 ---- @@ -4963,8 +4963,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta AttrNumber attnum; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; *************** DefineRelation(CreateStmt *stmt, char re -*** 444,449 **** ---- 446,461 ---- +*** 454,459 **** +--- 456,471 ---- localHasOids = interpretOidsOption(stmt->options); descriptor->tdhasoid = (localHasOids || parentOidCount > 0); @@ -4982,7 +4982,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta * Find columns with default values and prepare for insertion of the * defaults. Pre-cooked (that is, inherited) defaults go into a list of *************** DefineRelation(CreateStmt *stmt, char re -*** 513,519 **** +*** 523,529 **** parentOidCount, stmt->oncommit, reloptions, @@ -4990,7 +4990,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta StoreCatalogInheritance(relationId, inheritOids); ---- 525,532 ---- +--- 535,542 ---- parentOidCount, stmt->oncommit, reloptions, @@ -5000,8 +5000,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta StoreCatalogInheritance(relationId, inheritOids); *************** ExecuteTruncate(TruncateStmt *stmt) -*** 887,892 **** ---- 900,907 ---- +*** 897,902 **** +--- 910,917 ---- if (!pg_class_ownercheck(seq_relid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(seq_rel)); @@ -5011,8 +5011,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta seq_relids = lappend_oid(seq_relids, seq_relid); *************** truncate_check_rel(Relation rel) -*** 1042,1047 **** ---- 1057,1065 ---- +*** 1052,1057 **** +--- 1067,1075 ---- errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(rel)))); @@ -5023,8 +5023,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta * We can never allow truncation of shared or nailed-in-cache relations, * because we can't support changing their relfilenode values. *************** MergeAttributes(List *schema, List *supe -*** 1216,1221 **** ---- 1234,1241 ---- +*** 1226,1231 **** +--- 1244,1251 ---- if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); @@ -5034,8 +5034,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* * Reject duplications in the list of parents. *************** renameatt(Oid myrelid, -*** 1921,1926 **** ---- 1941,1949 ---- +*** 1931,1936 **** +--- 1951,1959 ---- errmsg("cannot rename system column \"%s\"", oldattname))); @@ -5046,8 +5046,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta * if the attribute is inherited, forbid the renaming, unless we are * already inside a recursive rename. *************** RenameRelation(Oid myrelid, const char * -*** 2026,2031 **** ---- 2049,2057 ---- +*** 2036,2041 **** +--- 2059,2067 ---- Oid namespaceId; char relkind; @@ -5058,7 +5058,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta * Grab an exclusive lock on the target table, index, sequence or view, * which we will NOT release until end of transaction. *************** ATPrepCmd(List **wqueue, Relation rel, A -*** 2359,2372 **** +*** 2369,2382 **** switch (cmd->subtype) { case AT_AddColumn: /* ADD COLUMN */ @@ -5073,7 +5073,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* Performs own recursion */ ATPrepAddColumn(wqueue, rel, recurse, cmd); pass = AT_PASS_ADD_COL; ---- 2385,2398 ---- +--- 2395,2408 ---- switch (cmd->subtype) { case AT_AddColumn: /* ADD COLUMN */ @@ -5089,7 +5089,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta ATPrepAddColumn(wqueue, rel, recurse, cmd); pass = AT_PASS_ADD_COL; *************** ATPrepCmd(List **wqueue, Relation rel, A -*** 2379,2397 **** +*** 2389,2407 **** * substitutes default values into INSERTs before it expands * rules. */ @@ -5109,7 +5109,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; ---- 2405,2423 ---- +--- 2415,2433 ---- * substitutes default values into INSERTs before it expands * rules. */ @@ -5130,7 +5130,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; *************** ATPrepCmd(List **wqueue, Relation rel, A -*** 2403,2415 **** +*** 2413,2425 **** pass = AT_PASS_COL_ATTRS; break; case AT_SetStorage: /* ALTER COLUMN STORAGE */ @@ -5144,7 +5144,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) ---- 2429,2441 ---- +--- 2439,2451 ---- pass = AT_PASS_COL_ATTRS; break; case AT_SetStorage: /* ALTER COLUMN STORAGE */ @@ -5159,7 +5159,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* No command-specific prep needed except saving recurse flag */ if (recurse) *************** ATPrepCmd(List **wqueue, Relation rel, A -*** 2417,2429 **** +*** 2427,2439 **** pass = AT_PASS_DROP; break; case AT_AddIndex: /* ADD INDEX */ @@ -5173,7 +5173,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) ---- 2443,2455 ---- +--- 2453,2465 ---- pass = AT_PASS_DROP; break; case AT_AddIndex: /* ADD INDEX */ @@ -5188,7 +5188,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* No command-specific prep needed except saving recurse flag */ if (recurse) *************** ATPrepCmd(List **wqueue, Relation rel, A -*** 2431,2437 **** +*** 2441,2447 **** pass = AT_PASS_ADD_CONSTR; break; case AT_DropConstraint: /* DROP CONSTRAINT */ @@ -5196,7 +5196,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) ---- 2457,2463 ---- +--- 2467,2473 ---- pass = AT_PASS_ADD_CONSTR; break; case AT_DropConstraint: /* DROP CONSTRAINT */ @@ -5205,7 +5205,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* No command-specific prep needed except saving recurse flag */ if (recurse) *************** ATPrepCmd(List **wqueue, Relation rel, A -*** 2439,2445 **** +*** 2449,2455 **** pass = AT_PASS_DROP; break; case AT_AlterColumnType: /* ALTER COLUMN TYPE */ @@ -5213,7 +5213,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* Performs own recursion */ ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd); pass = AT_PASS_ALTER_TYPE; ---- 2465,2471 ---- +--- 2475,2481 ---- pass = AT_PASS_DROP; break; case AT_AlterColumnType: /* ALTER COLUMN TYPE */ @@ -5222,7 +5222,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd); pass = AT_PASS_ALTER_TYPE; *************** ATPrepCmd(List **wqueue, Relation rel, A -*** 2451,2470 **** +*** 2461,2480 **** break; case AT_ClusterOn: /* CLUSTER ON */ case AT_DropCluster: /* SET WITHOUT CLUSTER */ @@ -5243,7 +5243,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* Performs own recursion */ if (rel->rd_rel->relhasoids) { ---- 2477,2496 ---- +--- 2487,2506 ---- break; case AT_ClusterOn: /* CLUSTER ON */ case AT_DropCluster: /* SET WITHOUT CLUSTER */ @@ -5265,7 +5265,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta if (rel->rd_rel->relhasoids) { *************** ATPrepCmd(List **wqueue, Relation rel, A -*** 2478,2491 **** +*** 2488,2501 **** pass = AT_PASS_DROP; break; case AT_SetTableSpace: /* SET TABLESPACE */ @@ -5280,7 +5280,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_MISC; ---- 2504,2517 ---- +--- 2514,2527 ---- pass = AT_PASS_DROP; break; case AT_SetTableSpace: /* SET TABLESPACE */ @@ -5296,7 +5296,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* No command-specific prep needed */ pass = AT_PASS_MISC; *************** ATPrepCmd(List **wqueue, Relation rel, A -*** 2504,2510 **** +*** 2514,2520 **** case AT_DisableRule: case AT_AddInherit: /* INHERIT / NO INHERIT */ case AT_DropInherit: @@ -5304,7 +5304,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* These commands never recurse */ /* No command-specific prep needed */ pass = AT_PASS_MISC; ---- 2530,2536 ---- +--- 2540,2546 ---- case AT_DisableRule: case AT_AddInherit: /* INHERIT / NO INHERIT */ case AT_DropInherit: @@ -5313,7 +5313,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* No command-specific prep needed */ pass = AT_PASS_MISC; *************** ATRewriteTables(List **wqueue) -*** 2850,2857 **** +*** 2860,2867 **** /* * The new relation is local to our transaction and we know * nothing depends on it, so DROP_RESTRICT should be OK. @@ -5322,7 +5322,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* performDeletion does CommandCounterIncrement at end */ /* ---- 2876,2884 ---- +--- 2886,2894 ---- /* * The new relation is local to our transaction and we know * nothing depends on it, so DROP_RESTRICT should be OK. @@ -5333,8 +5333,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* *************** ATRewriteTable(AlteredTableInfo *tab, Oi -*** 3076,3086 **** ---- 3103,3116 ---- +*** 3086,3096 **** +--- 3113,3126 ---- if (newrel) { Oid tupOid = InvalidOid; @@ -5350,8 +5350,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* Set dropped attributes to null in new tuple */ foreach(lc, dropped_attrs) *************** ATRewriteTable(AlteredTableInfo *tab, Oi -*** 3112,3117 **** ---- 3142,3150 ---- +*** 3122,3127 **** +--- 3152,3160 ---- /* Preserve OID, if any */ if (newTupDesc->tdhasoid) HeapTupleSetOid(tuple, tupOid); @@ -5362,7 +5362,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* Now check any constraints on the possibly-changed tuple */ *************** ATGetQueueEntry(List **wqueue, Relation -*** 3213,3219 **** +*** 3223,3229 **** * - Ensure that it is not a system table */ static void @@ -5370,7 +5370,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta { if (rel->rd_rel->relkind != RELKIND_RELATION) { ---- 3246,3252 ---- +--- 3256,3262 ---- * - Ensure that it is not a system table */ static void @@ -5379,8 +5379,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta if (rel->rd_rel->relkind != RELKIND_RELATION) { *************** ATSimplePermissions(Relation rel, bool a -*** 3237,3242 **** ---- 3270,3281 ---- +*** 3247,3252 **** +--- 3280,3291 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); @@ -5394,7 +5394,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), *************** ATSimplePermissions(Relation rel, bool a -*** 3252,3258 **** +*** 3262,3268 **** * - Ensure that it is not a system table */ static void @@ -5402,7 +5402,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta { if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_INDEX) ---- 3291,3297 ---- +--- 3301,3307 ---- * - Ensure that it is not a system table */ static void @@ -5411,8 +5411,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_INDEX) *************** ATSimplePermissionsRelationOrIndex(Relat -*** 3266,3271 **** ---- 3305,3316 ---- +*** 3276,3281 **** +--- 3315,3326 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); @@ -5426,8 +5426,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), *************** ATExecAddColumn(AlteredTableInfo *tab, R -*** 3509,3514 **** ---- 3554,3560 ---- +*** 3519,3524 **** +--- 3564,3570 ---- HeapTuple typeTuple; Oid typeOid; int32 typmod; @@ -5436,8 +5436,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta Expr *defval; *************** ATExecAddColumn(AlteredTableInfo *tab, R -*** 3546,3551 **** ---- 3592,3600 ---- +*** 3556,3561 **** +--- 3602,3610 ---- errmsg("child table \"%s\" has a conflicting \"%s\" column", RelationGetRelationName(rel), colDef->colname))); @@ -5448,8 +5448,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta childatt->attinhcount++; simple_heap_update(attrdesc, &tuple->t_self, tuple); *************** ATExecAddColumn(AlteredTableInfo *tab, R -*** 3585,3590 **** ---- 3634,3642 ---- +*** 3595,3600 **** +--- 3644,3652 ---- errmsg("column \"%s\" of relation \"%s\" already exists", colDef->colname, RelationGetRelationName(rel)))); @@ -5460,7 +5460,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta if (isOid) newattnum = ObjectIdAttributeNumber; *************** ATExecAddColumn(AlteredTableInfo *tab, R -*** 3627,3633 **** +*** 3637,3643 **** ReleaseSysCache(typeTuple); @@ -5468,7 +5468,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta heap_close(attrdesc, RowExclusiveLock); ---- 3679,3685 ---- +--- 3689,3695 ---- ReleaseSysCache(typeTuple); @@ -5477,8 +5477,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta heap_close(attrdesc, RowExclusiveLock); *************** ATPrepSetStatistics(Relation rel, const -*** 4016,4021 **** ---- 4068,4075 ---- +*** 4026,4031 **** +--- 4078,4085 ---- if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); @@ -5488,7 +5488,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta static void *************** ATExecDropColumn(List **wqueue, Relation -*** 4171,4177 **** +*** 4181,4187 **** /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) @@ -5496,7 +5496,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* * get the number of the attribute ---- 4225,4231 ---- +--- 4235,4241 ---- /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) @@ -5505,7 +5505,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* * get the number of the attribute *************** ATAddCheckConstraint(List **wqueue, Alte -*** 4473,4479 **** +*** 4483,4489 **** /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) @@ -5513,7 +5513,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* * Call AddRelationNewConstraints to do the work, making sure it works on ---- 4527,4533 ---- +--- 4537,4543 ---- /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) @@ -5522,7 +5522,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* * Call AddRelationNewConstraints to do the work, making sure it works on *************** checkFkeyPermissions(Relation rel, int16 -*** 5102,5108 **** +*** 5112,5118 **** aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid, ACL_REFERENCES); if (aclresult == ACLCHECK_OK) @@ -5530,7 +5530,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* Else we must have REFERENCES on each column */ for (i = 0; i < natts; i++) { ---- 5156,5162 ---- +--- 5166,5172 ---- aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid, ACL_REFERENCES); if (aclresult == ACLCHECK_OK) @@ -5539,8 +5539,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta for (i = 0; i < natts; i++) { *************** checkFkeyPermissions(Relation rel, int16 -*** 5112,5117 **** ---- 5166,5174 ---- +*** 5122,5127 **** +--- 5176,5184 ---- aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); } @@ -5551,7 +5551,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* *************** ATExecDropConstraint(Relation rel, const -*** 5375,5381 **** +*** 5385,5391 **** /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) @@ -5559,7 +5559,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta conrel = heap_open(ConstraintRelationId, RowExclusiveLock); ---- 5432,5438 ---- +--- 5442,5448 ---- /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) @@ -5568,8 +5568,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta conrel = heap_open(ConstraintRelationId, RowExclusiveLock); *************** ATExecChangeOwner(Oid relationOid, Oid n -*** 6308,6313 **** ---- 6365,6372 ---- +*** 6318,6323 **** +--- 6375,6382 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } @@ -5579,7 +5579,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta memset(repl_null, false, sizeof(repl_null)); *************** ATExecAddInherit(Relation child_rel, Ran -*** 6912,6918 **** +*** 6922,6928 **** * Must be owner of both parent and child -- child was checked by * ATSimplePermissions call in ATPrepCmd */ @@ -5587,7 +5587,7 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* Permanent rels cannot inherit from temporary ones */ if (parent_rel->rd_istemp && !child_rel->rd_istemp) ---- 6971,6977 ---- +--- 6981,6987 ---- * Must be owner of both parent and child -- child was checked by * ATSimplePermissions call in ATPrepCmd */ @@ -5596,8 +5596,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* Permanent rels cannot inherit from temporary ones */ if (parent_rel->rd_istemp && !child_rel->rd_istemp) *************** AlterTableNamespace(RangeVar *relation, -*** 7570,7575 **** ---- 7629,7637 ---- +*** 7580,7585 **** +--- 7639,7647 ---- RelationGetRelationName(rel), newschema))); @@ -5608,8 +5608,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid)) ereport(ERROR, *************** AlterSeqNamespaces(Relation classRel, Re -*** 7762,7767 **** ---- 7824,7957 ---- +*** 7772,7777 **** +--- 7834,7967 ---- relation_close(depRel, AccessShareLock); } @@ -5745,8 +5745,8 @@ diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/ta /* * This code supports diff -Nrpc base/src/backend/commands/trigger.c sepgsql/src/backend/commands/trigger.c -*** base/src/backend/commands/trigger.c Fri Jun 19 13:40:37 2009 ---- sepgsql/src/backend/commands/trigger.c Thu Sep 17 22:10:19 2009 +*** base/src/backend/commands/trigger.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/commands/trigger.c Tue Dec 15 17:30:25 2009 *************** *** 33,38 **** --- 33,39 ---- @@ -6187,8 +6187,8 @@ diff -Nrpc base/src/backend/executor/execJunk.c sepgsql/src/backend/executor/exe /* * Use the given slot, or make a new slot if we weren't given one. diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/execMain.c -*** base/src/backend/executor/execMain.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/executor/execMain.c Thu Oct 8 09:29:32 2009 +*** base/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 ---- @@ -6487,8 +6487,8 @@ diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/exe Oid intoRelationId; TupleDesc tupdesc; *************** OpenIntoRel(QueryDesc *queryDesc) -*** 2871,2876 **** ---- 2982,2995 ---- +*** 2886,2891 **** +--- 2997,3010 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); @@ -6504,7 +6504,7 @@ diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/exe * Select tablespace to use. If not specified, use default tablespace * (which may in turn default to database's default). *************** OpenIntoRel(QueryDesc *queryDesc) -*** 2929,2935 **** +*** 2944,2950 **** 0, into->onCommit, reloptions, @@ -6512,7 +6512,7 @@ diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/exe FreeTupleDesc(tupdesc); ---- 3048,3055 ---- +--- 3063,3070 ---- 0, into->onCommit, reloptions, @@ -6522,8 +6522,8 @@ diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/exe FreeTupleDesc(tupdesc); *************** intorel_receive(TupleTableSlot *slot, De -*** 3054,3059 **** ---- 3174,3183 ---- +*** 3069,3074 **** +--- 3189,3198 ---- if (myState->rel->rd_rel->relhasoids) HeapTupleSetOid(tuple, InvalidOid); @@ -7003,8 +7003,8 @@ diff -Nrpc base/src/backend/executor/nodeWindowAgg.c sepgsql/src/backend/executo } diff -Nrpc base/src/backend/executor/spi.c sepgsql/src/backend/executor/spi.c -*** base/src/backend/executor/spi.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/executor/spi.c Tue Sep 8 23:55:48 2009 +*** base/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 ---- @@ -7160,8 +7160,8 @@ diff -Nrpc base/src/backend/libpq/be-fsstubs.c sepgsql/src/backend/libpq/be-fsst * prepares large objects for transaction commit */ diff -Nrpc base/src/backend/nodes/copyfuncs.c sepgsql/src/backend/nodes/copyfuncs.c -*** base/src/backend/nodes/copyfuncs.c Fri Jun 19 13:40:37 2009 ---- sepgsql/src/backend/nodes/copyfuncs.c Wed Jul 15 19:39:56 2009 +*** base/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 ---- @@ -7176,7 +7176,7 @@ diff -Nrpc base/src/backend/nodes/copyfuncs.c sepgsql/src/backend/nodes/copyfunc *** 2075,2080 **** --- 2076,2082 ---- COPY_NODE_FIELD(raw_default); - COPY_STRING_FIELD(cooked_default); + COPY_NODE_FIELD(cooked_default); COPY_NODE_FIELD(constraints); + COPY_NODE_FIELD(secLabel); @@ -7239,8 +7239,8 @@ diff -Nrpc base/src/backend/nodes/copyfuncs.c sepgsql/src/backend/nodes/copyfunc retval = _copyRuleStmt(from); break; diff -Nrpc base/src/backend/nodes/equalfuncs.c sepgsql/src/backend/nodes/equalfuncs.c -*** base/src/backend/nodes/equalfuncs.c Fri Jun 19 13:40:37 2009 ---- sepgsql/src/backend/nodes/equalfuncs.c Wed Jul 15 19:37:35 2009 +*** base/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 ---- @@ -7287,7 +7287,7 @@ diff -Nrpc base/src/backend/nodes/equalfuncs.c sepgsql/src/backend/nodes/equalfu *** 2054,2059 **** --- 2069,2075 ---- COMPARE_NODE_FIELD(raw_default); - COMPARE_STRING_FIELD(cooked_default); + COMPARE_NODE_FIELD(cooked_default); COMPARE_NODE_FIELD(constraints); + COMPARE_NODE_FIELD(secLabel); @@ -7306,8 +7306,8 @@ diff -Nrpc base/src/backend/nodes/equalfuncs.c sepgsql/src/backend/nodes/equalfu retval = _equalRuleStmt(a, b); break; diff -Nrpc base/src/backend/nodes/outfuncs.c sepgsql/src/backend/nodes/outfuncs.c -*** base/src/backend/nodes/outfuncs.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/nodes/outfuncs.c Wed Jul 15 19:39:56 2009 +*** base/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 ---- @@ -7342,7 +7342,7 @@ diff -Nrpc base/src/backend/nodes/outfuncs.c sepgsql/src/backend/nodes/outfuncs. *** 1839,1844 **** --- 1842,1848 ---- WRITE_NODE_FIELD(raw_default); - WRITE_STRING_FIELD(cooked_default); + WRITE_NODE_FIELD(cooked_default); WRITE_NODE_FIELD(constraints); + WRITE_NODE_FIELD(secLabel); } @@ -7420,8 +7420,8 @@ diff -Nrpc base/src/backend/optimizer/util/relnode.c sepgsql/src/backend/optimiz /* Check type of rtable entry */ switch (rte->rtekind) diff -Nrpc base/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze.c -*** base/src/backend/parser/analyze.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/parser/analyze.c Sun Sep 6 19:53:10 2009 +*** base/src/backend/parser/analyze.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/parser/analyze.c Tue Dec 15 17:30:25 2009 *************** *** 25,30 **** --- 25,31 ---- @@ -7433,7 +7433,7 @@ diff -Nrpc base/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze. #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" *************** transformInsertStmt(ParseState *pstate, -*** 653,659 **** +*** 660,666 **** tle = makeTargetEntry(expr, attr_num, col->name, @@ -7441,7 +7441,7 @@ diff -Nrpc base/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze. qry->targetList = lappend(qry->targetList, tle); rte->modifiedCols = bms_add_member(rte->modifiedCols, ---- 654,660 ---- +--- 661,667 ---- tle = makeTargetEntry(expr, attr_num, col->name, @@ -7450,8 +7450,8 @@ diff -Nrpc base/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze. rte->modifiedCols = bms_add_member(rte->modifiedCols, *************** transformInsertRow(ParseState *pstate, L -*** 768,773 **** ---- 769,816 ---- +*** 775,780 **** +--- 776,823 ---- return result; } @@ -7501,8 +7501,8 @@ diff -Nrpc base/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze. /* * transformSelectStmt - *************** transformSelectStmt(ParseState *pstate, -*** 872,877 **** ---- 915,921 ---- +*** 879,884 **** +--- 922,928 ---- if (stmt->intoClause) { qry->intoClause = stmt->intoClause; @@ -7971,8 +7971,8 @@ diff -Nrpc base/src/backend/parser/parse_target.c sepgsql/src/backend/parser/par /* * Check for duplicates, but only of whole columns --- we allow diff -Nrpc base/src/backend/parser/parse_utilcmd.c sepgsql/src/backend/parser/parse_utilcmd.c -*** base/src/backend/parser/parse_utilcmd.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/parser/parse_utilcmd.c Fri Sep 18 14:51:00 2009 +*** base/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 ---- @@ -8015,8 +8015,8 @@ diff -Nrpc base/src/backend/postmaster/autovacuum.c sepgsql/src/backend/postmast else { diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmaster/postmaster.c -*** base/src/backend/postmaster/postmaster.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/postmaster/postmaster.c Tue Dec 8 14:04:25 2009 +*** base/src/backend/postmaster/postmaster.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/postmaster/postmaster.c Tue Dec 15 17:30:25 2009 *************** *** 108,113 **** --- 108,114 ---- @@ -8070,8 +8070,8 @@ diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmast if (avlauncher_needs_signal) { *************** SIGHUP_handler(SIGNAL_ARGS) -*** 2053,2058 **** ---- 2061,2068 ---- +*** 2055,2060 **** +--- 2063,2070 ---- signal_child(SysLoggerPID, SIGHUP); if (PgStatPID != 0) signal_child(PgStatPID, SIGHUP); @@ -8081,8 +8081,8 @@ diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmast /* Reload authentication config files too */ if (!load_hba()) *************** pmdie(SIGNAL_ARGS) -*** 2113,2118 **** ---- 2123,2131 ---- +*** 2115,2120 **** +--- 2125,2133 ---- /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); @@ -8093,8 +8093,8 @@ diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmast } *************** pmdie(SIGNAL_ARGS) -*** 2160,2165 **** ---- 2173,2181 ---- +*** 2162,2167 **** +--- 2175,2183 ---- /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); @@ -8105,8 +8105,8 @@ diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmast } *************** pmdie(SIGNAL_ARGS) -*** 2193,2198 **** ---- 2209,2216 ---- +*** 2195,2200 **** +--- 2211,2218 ---- signal_child(PgArchPID, SIGQUIT); if (PgStatPID != 0) signal_child(PgStatPID, SIGQUIT); @@ -8116,8 +8116,8 @@ diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmast break; } *************** reaper(SIGNAL_ARGS) -*** 2455,2460 **** ---- 2473,2488 ---- +*** 2457,2462 **** +--- 2475,2490 ---- continue; } @@ -8135,8 +8135,8 @@ diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmast * Else do standard backend child cleanup. */ *************** HandleChildCrash(int pid, int exitstatus -*** 2646,2651 **** ---- 2674,2691 ---- +*** 2648,2653 **** +--- 2676,2693 ---- signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); } @@ -8156,7 +8156,7 @@ diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmast * Force a power-cycle of the pgarch process too. (This isn't absolutely * necessary, but it seems like a good idea for robustness, and it *************** PostmasterStateMachine(void) -*** 2778,2784 **** +*** 2780,2786 **** StartupPID == 0 && (BgWriterPID == 0 || !FatalError) && WalWriterPID == 0 && @@ -8164,7 +8164,7 @@ diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmast { if (FatalError) { ---- 2818,2825 ---- +--- 2820,2827 ---- StartupPID == 0 && (BgWriterPID == 0 || !FatalError) && WalWriterPID == 0 && @@ -8174,8 +8174,8 @@ diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmast if (FatalError) { *************** StartChildProcess(AuxProcType type) -*** 4321,4326 **** ---- 4362,4373 ---- +*** 4323,4328 **** +--- 4364,4375 ---- ereport(LOG, (errmsg("could not fork WAL writer process: %m"))); break; @@ -8408,10 +8408,10 @@ diff -Nrpc base/src/backend/security/sepgsql/Makefile sepgsql/src/backend/securi + include $(top_srcdir)/src/backend/common.mk diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/sepgsql/avc.c *** base/src/backend/security/sepgsql/avc.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/avc.c Tue Dec 8 14:16:15 2009 +--- sepgsql/src/backend/security/sepgsql/avc.c Thu Dec 10 10:36:18 2009 *************** *** 0 **** ---- 1,880 ---- +--- 1,881 ---- + /* + * src/backend/security/sepgsql/avc.c + * SE-PostgreSQL userspace access vector cache @@ -8454,7 +8454,7 @@ diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/ + * postmaster can receives the notification messages from the kernel + * space, and invalidate the current version of avc. + */ -+ static MemoryContext AvcMemCtx; ++ static MemoryContext AvcMemCtx = NULL; + + #define AVC_HASH_NUM_SLOTS 256 + #define AVC_HASH_NUM_NODES 180 @@ -8554,11 +8554,12 @@ diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/ + * 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()) ++ if (!sepgsqlIsEnabled() || !AvcMemCtx) + return; + + MemoryContextReset(AvcMemCtx); @@ -15175,11 +15176,11 @@ diff -Nrpc base/src/backend/security/sepgsql/policy/sepostgresql-devel.te sepgsq + allow sepgsql_database_type user_home_t : db_database { load_module }; + ') diff -Nrpc base/src/backend/storage/file/fd.c sepgsql/src/backend/storage/file/fd.c -*** base/src/backend/storage/file/fd.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/storage/file/fd.c Wed Jul 15 19:48:58 2009 +*** base/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) -*** 1319,1324 **** ---- 1319,1331 ---- +*** 1329,1334 **** +--- 1329,1341 ---- return returnCode; } @@ -15496,8 +15497,8 @@ diff -Nrpc base/src/backend/tcop/pquery.c sepgsql/src/backend/tcop/pquery.c /* diff -Nrpc base/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c -*** base/src/backend/tcop/utility.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/tcop/utility.c Wed Jul 15 21:17:18 2009 +*** base/src/backend/tcop/utility.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/tcop/utility.c Tue Dec 15 17:30:25 2009 *************** *** 50,55 **** --- 50,56 ---- @@ -15519,8 +15520,8 @@ diff -Nrpc base/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c case T_AlterTableStmt: case T_RenameStmt: *************** ProcessUtility(Node *parsetree, -*** 612,617 **** ---- 614,623 ---- +*** 634,639 **** +--- 636,645 ---- ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); break; @@ -15532,8 +15533,8 @@ diff -Nrpc base/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c { List *stmts; *************** ProcessUtility(Node *parsetree, -*** 893,898 **** ---- 899,905 ---- +*** 917,922 **** +--- 923,929 ---- LoadStmt *stmt = (LoadStmt *) parsetree; closeAllVfds(); /* probably not necessary... */ @@ -15542,8 +15543,8 @@ diff -Nrpc base/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c load_file(stmt->filename, !superuser()); } *************** CreateCommandTag(Node *parsetree) -*** 1635,1640 **** ---- 1642,1672 ---- +*** 1661,1666 **** +--- 1668,1698 ---- } break; @@ -15576,8 +15577,8 @@ diff -Nrpc base/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c switch (((AlterTableStmt *) parsetree)->relkind) { *************** GetCommandLogLevel(Node *parsetree) -*** 2213,2218 **** ---- 2245,2254 ---- +*** 2239,2244 **** +--- 2271,2280 ---- lev = LOGSTMT_DDL; break; @@ -15625,8 +15626,8 @@ diff -Nrpc base/src/backend/utils/adt/genfile.c sepgsql/src/backend/utils/adt/ge if (stat(filename, &fst) < 0) ereport(ERROR, diff -Nrpc base/src/backend/utils/adt/ri_triggers.c sepgsql/src/backend/utils/adt/ri_triggers.c -*** base/src/backend/utils/adt/ri_triggers.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/utils/adt/ri_triggers.c Wed Jul 15 19:39:56 2009 +*** base/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 ---- @@ -15674,20 +15675,20 @@ diff -Nrpc base/src/backend/utils/adt/ri_triggers.c sepgsql/src/backend/utils/ad if (spi_result != SPI_OK_SELECT) elog(ERROR, "SPI_execute_snapshot returned %d", spi_result); *************** ri_PerformCheck(RI_QueryKey *qkey, SPIPl -*** 3264,3269 **** ---- 3274,3280 ---- +*** 3265,3270 **** +--- 3275,3281 ---- int spi_result; Oid save_userid; - bool save_secdefcxt; + 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 -*** 3346,3357 **** ---- 3357,3375 ---- - GetUserIdAndContext(&save_userid, &save_secdefcxt); - SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true); +*** 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); @@ -15702,8 +15703,8 @@ diff -Nrpc base/src/backend/utils/adt/ri_triggers.c sepgsql/src/backend/utils/ad + /* Restore Row-level stuff behavior */ + rowlvSetPerformingMode(save_rowlv); + - /* Restore UID */ - SetUserIdAndContext(save_userid, save_secdefcxt); + /* Restore UID and security context */ + SetUserIdAndSecContext(save_userid, save_sec_context); diff -Nrpc base/src/backend/utils/adt/tid.c sepgsql/src/backend/utils/adt/tid.c *** base/src/backend/utils/adt/tid.c Sat Jan 3 13:01:35 2009 @@ -15807,8 +15808,8 @@ diff -Nrpc base/src/backend/utils/cache/plancache.c sepgsql/src/backend/utils/ca /* other cases shouldn't happen, but return NULL */ break; diff -Nrpc base/src/backend/utils/cache/relcache.c sepgsql/src/backend/utils/cache/relcache.c -*** base/src/backend/utils/cache/relcache.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/utils/cache/relcache.c Wed Sep 9 13:14:37 2009 +*** base/src/backend/utils/cache/relcache.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/utils/cache/relcache.c Tue Dec 15 17:30:25 2009 *************** *** 47,52 **** --- 47,53 ---- @@ -15833,8 +15834,8 @@ diff -Nrpc base/src/backend/utils/cache/relcache.c sepgsql/src/backend/utils/cac * initialize the relation lock manager information */ *************** formrdesc(const char *relationName, Oid -*** 1458,1463 **** ---- 1463,1473 ---- +*** 1460,1465 **** +--- 1465,1475 ---- RelationGetRelid(relation) = relation->rd_att->attrs[0]->attrelid; relation->rd_rel->relfilenode = RelationGetRelid(relation); @@ -15847,8 +15848,8 @@ diff -Nrpc base/src/backend/utils/cache/relcache.c sepgsql/src/backend/utils/cac * initialize the relation lock manager information */ *************** BuildHardcodedDescriptor(int natts, Form -*** 2699,2704 **** ---- 2709,2721 ---- +*** 2749,2754 **** +--- 2759,2771 ---- result = CreateTemplateTupleDesc(natts, hasoids); result->tdtypeid = RECORDOID; /* not right, but we don't care */ result->tdtypmod = -1; @@ -15863,8 +15864,8 @@ diff -Nrpc base/src/backend/utils/cache/relcache.c sepgsql/src/backend/utils/cac for (i = 0; i < natts; i++) { *************** load_relcache_init_file(void) -*** 3453,3458 **** ---- 3470,3480 ---- +*** 3503,3508 **** +--- 3520,3530 ---- rel->rd_options = NULL; } @@ -15970,8 +15971,8 @@ diff -Nrpc base/src/backend/utils/fmgr/dfmgr.c sepgsql/src/backend/utils/fmgr/df internal_unload_library(fullname); diff -Nrpc base/src/backend/utils/fmgr/fmgr.c sepgsql/src/backend/utils/fmgr/fmgr.c -*** base/src/backend/utils/fmgr/fmgr.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/utils/fmgr/fmgr.c Thu Sep 17 17:04:16 2009 +*** base/src/backend/utils/fmgr/fmgr.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/utils/fmgr/fmgr.c Tue Dec 15 17:30:25 2009 *************** *** 24,29 **** --- 24,30 ---- @@ -16047,8 +16048,8 @@ diff -Nrpc base/src/backend/utils/init/postinit.c sepgsql/src/backend/utils/init * Figure out our postgres user id, and see if we are a superuser. * diff -Nrpc base/src/backend/utils/misc/guc.c sepgsql/src/backend/utils/misc/guc.c -*** base/src/backend/utils/misc/guc.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/utils/misc/guc.c Sun Sep 6 19:53:10 2009 +*** base/src/backend/utils/misc/guc.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/utils/misc/guc.c Tue Dec 15 17:30:25 2009 *************** *** 56,61 **** --- 56,62 ---- @@ -16178,8 +16179,8 @@ diff -Nrpc base/src/backend/utils/misc/superuser.c sepgsql/src/backend/utils/mis } diff -Nrpc base/src/bin/initdb/initdb.c sepgsql/src/bin/initdb/initdb.c -*** base/src/bin/initdb/initdb.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/bin/initdb/initdb.c Wed Jul 15 19:35:52 2009 +*** base/src/bin/initdb/initdb.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/bin/initdb/initdb.c Tue Dec 15 17:30:25 2009 *************** static bool debug = false; *** 87,92 **** --- 87,93 ---- @@ -16239,8 +16240,8 @@ diff -Nrpc base/src/bin/initdb/initdb.c sepgsql/src/bin/initdb/initdb.c show_setting = true; break; diff -Nrpc base/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c -*** base/src/bin/pg_dump/pg_dump.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/bin/pg_dump/pg_dump.c Tue Sep 8 17:13:41 2009 +*** base/src/bin/pg_dump/pg_dump.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/bin/pg_dump/pg_dump.c Tue Dec 15 17:30:25 2009 *************** static int disable_dollar_quoting = 0; *** 112,117 **** --- 112,119 ---- @@ -16967,8 +16968,8 @@ diff -Nrpc base/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c /* *************** fmtCopyColumnList(const TableInfo *ti) -*** 11508,11513 **** ---- 11595,11607 ---- +*** 11510,11515 **** +--- 11597,11609 ---- appendPQExpBuffer(q, "("); needComma = false; @@ -17914,21 +17915,21 @@ diff -Nrpc base/src/include/nodes/nodes.h sepgsql/src/include/nodes/nodes.h /* * TAGS FOR PARSE TREE NODES (parsenodes.h) diff -Nrpc base/src/include/nodes/parsenodes.h sepgsql/src/include/nodes/parsenodes.h -*** base/src/include/nodes/parsenodes.h Fri Jun 19 13:40:37 2009 ---- sepgsql/src/include/nodes/parsenodes.h Wed Jul 15 19:37:35 2009 +*** base/src/include/nodes/parsenodes.h Tue Dec 15 17:16:51 2009 +--- sepgsql/src/include/nodes/parsenodes.h Tue Dec 15 17:30:25 2009 *************** typedef struct ColumnDef -*** 464,469 **** ---- 464,470 ---- +*** 463,468 **** +--- 463,469 ---- Node *raw_default; /* default value (untransformed parse tree) */ - char *cooked_default; /* nodeToString representation */ + Node *cooked_default; /* default value (transformed expr tree) */ List *constraints; /* other constraints on column */ + Node *secLabel; /* security label of column */ } ColumnDef; /* *************** typedef struct CreateSchemaStmt -*** 1070,1075 **** ---- 1071,1077 ---- +*** 1069,1074 **** +--- 1070,1076 ---- NodeTag type; char *schemaname; /* the name of the schema to create */ char *authid; /* the owner of the created schema */ @@ -17937,8 +17938,8 @@ diff -Nrpc base/src/include/nodes/parsenodes.h sepgsql/src/include/nodes/parseno } CreateSchemaStmt; *************** typedef struct CreateStmt -*** 1335,1340 **** ---- 1337,1343 ---- +*** 1334,1339 **** +--- 1336,1342 ---- List *options; /* options from WITH clause */ OnCommitAction oncommit; /* what do we do at COMMIT? */ char *tablespacename; /* table space to use, or NULL */ @@ -17947,8 +17948,8 @@ diff -Nrpc base/src/include/nodes/parsenodes.h sepgsql/src/include/nodes/parseno /* ---------- *************** typedef struct CreateSeqStmt -*** 1639,1644 **** ---- 1642,1648 ---- +*** 1638,1643 **** +--- 1641,1647 ---- NodeTag type; RangeVar *sequence; /* the sequence to create */ List *options; @@ -17957,8 +17958,8 @@ diff -Nrpc base/src/include/nodes/parsenodes.h sepgsql/src/include/nodes/parseno typedef struct AlterSeqStmt *************** typedef struct AlterOwnerStmt -*** 1993,1998 **** ---- 1997,2016 ---- +*** 1992,1997 **** +--- 1996,2015 ---- char *newowner; /* the new owner */ } AlterOwnerStmt; @@ -19046,8 +19047,8 @@ diff -Nrpc base/src/test/regress/expected/sanity_check.out sepgsql/src/test/regr -- -- another sanity check: every system catalog that has OIDs should have diff -Nrpc base/src/test/regress/pg_regress.c sepgsql/src/test/regress/pg_regress.c -*** base/src/test/regress/pg_regress.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/test/regress/pg_regress.c Tue Dec 1 17:11:40 2009 +*** base/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 ---- diff --git a/sepostgresql.spec b/sepostgresql.spec index 1c85cb6..5711591 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -11,8 +11,8 @@ Summary: Security Enhanced PostgreSQL Name: sepostgresql -Version: 8.4.1 -Release: 2464%{?dist} +Version: 8.4.2 +Release: 2487%{?dist} License: BSD Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ @@ -192,6 +192,9 @@ fi %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog +* Wed Dec 16 2009 KaiGai Kohei - 8.4.2-2487 +- upgrade base version 8.4.1->8.4.2 + * Fri Dec 8 2009 KaiGai Kohei - 8.4.1-2464 - rework: backport features from v8.5devel tree - fixbug: selinux netlink receiver process didn't have correct ps display diff --git a/sources b/sources index 91e78c6..0aef01a 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -f2015af17bacbbfe140daf0d1067f9c9 postgresql-8.4.1.tar.bz2 +d738227e2f1f742d2f2d4ab56496c5c6 postgresql-8.4.2.tar.bz2 From 7748c46cb98b12031a56065deb0c121df7a60700 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Mon, 15 Feb 2010 07:37:21 +0000 Subject: [PATCH 04/24] - fix: build failed due to an implicit header file include - update: feature backport from v8.5 development --- pgsql-01-8.4-blobs.patch | 2210 ++++ pgsql-02-8.4-sepgsql.patch | 17591 ++++++++++++++++++++++++++++++++ sepostgresql-8.4.patch | 19117 ----------------------------------- sepostgresql.spec | 12 +- 4 files changed, 19810 insertions(+), 19120 deletions(-) create mode 100644 pgsql-01-8.4-blobs.patch create mode 100644 pgsql-02-8.4-sepgsql.patch delete mode 100644 sepostgresql-8.4.patch diff --git a/pgsql-01-8.4-blobs.patch b/pgsql-01-8.4-blobs.patch new file mode 100644 index 0000000..fefc54a --- /dev/null +++ b/pgsql-01-8.4-blobs.patch @@ -0,0 +1,2210 @@ +diff --git a/contrib/lo/lo_test.sql b/contrib/lo/lo_test.sql +index aac0e99..b9ae89c 100644 +--- a/contrib/lo/lo_test.sql ++++ b/contrib/lo/lo_test.sql +@@ -12,7 +12,7 @@ SET search_path = public; + -- + + -- Check what is in pg_largeobject +-SELECT count(DISTINCT loid) FROM pg_largeobject; ++SELECT count(oid) FROM pg_largeobject_metadata; + + -- ignore any errors here - simply drop the table if it already exists + DROP TABLE a; +@@ -74,6 +74,6 @@ DELETE FROM a; + DROP TABLE a; + + -- Check what is in pg_largeobject ... if different from original, trouble +-SELECT count(DISTINCT loid) FROM pg_largeobject; ++SELECT count(oid) FROM pg_largeobject_metadata; + + -- end of tests +diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c +index 3793cde..acc94cc 100644 +--- a/contrib/vacuumlo/vacuumlo.c ++++ b/contrib/vacuumlo/vacuumlo.c +@@ -142,7 +142,10 @@ vacuumlo(char *database, struct _param * param) + */ + buf[0] = '\0'; + strcat(buf, "CREATE TEMP TABLE vacuum_l AS "); +- strcat(buf, "SELECT DISTINCT loid AS lo FROM pg_largeobject "); ++ if (PQserverVersion(conn) >= 80500) ++ strcat(buf, "SELECT oid AS lo FROM pg_largeobject_metadata"); ++ else ++ strcat(buf, "SELECT DISTINCT loid AS lo FROM pg_largeobject"); + res = PQexec(conn, buf); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { +diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile +index 400ae80..3644ca6 100644 +--- a/src/backend/catalog/Makefile ++++ b/src/backend/catalog/Makefile +@@ -29,9 +29,9 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ + pg_proc.h pg_type.h pg_attribute.h pg_class.h \ + pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \ + pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \ +- pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ +- pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \ +- pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ ++ pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \ ++ pg_statistic.h pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h \ ++ pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ + pg_database.h pg_tablespace.h pg_pltemplate.h \ + pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ + pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ +diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c +index ec4aaf0..1be417c 100644 +--- a/src/backend/catalog/aclchk.c ++++ b/src/backend/catalog/aclchk.c +@@ -30,6 +30,8 @@ + #include "catalog/pg_foreign_data_wrapper.h" + #include "catalog/pg_foreign_server.h" + #include "catalog/pg_language.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_largeobject_metadata.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_opclass.h" + #include "catalog/pg_operator.h" +@@ -57,6 +59,7 @@ static void ExecGrant_Fdw(InternalGrant *grantStmt); + static void ExecGrant_ForeignServer(InternalGrant *grantStmt); + static void ExecGrant_Function(InternalGrant *grantStmt); + static void ExecGrant_Language(InternalGrant *grantStmt); ++static void ExecGrant_Largeobject(InternalGrant *grantStmt); + static void ExecGrant_Namespace(InternalGrant *grantStmt); + static void ExecGrant_Tablespace(InternalGrant *grantStmt); + +@@ -200,6 +203,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs, + case ACL_KIND_LANGUAGE: + whole_mask = ACL_ALL_RIGHTS_LANGUAGE; + break; ++ case ACL_KIND_LARGEOBJECT: ++ whole_mask = ACL_ALL_RIGHTS_LARGEOBJECT; ++ break; + case ACL_KIND_NAMESPACE: + whole_mask = ACL_ALL_RIGHTS_NAMESPACE; + break; +@@ -344,6 +350,10 @@ ExecuteGrantStmt(GrantStmt *stmt) + all_privileges = ACL_ALL_RIGHTS_LANGUAGE; + errormsg = gettext_noop("invalid privilege type %s for language"); + break; ++ case ACL_OBJECT_LARGEOBJECT: ++ all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT; ++ errormsg = gettext_noop("invalid privilege type %s for large object"); ++ break; + case ACL_OBJECT_NAMESPACE: + all_privileges = ACL_ALL_RIGHTS_NAMESPACE; + errormsg = gettext_noop("invalid privilege type %s for schema"); +@@ -449,6 +459,9 @@ ExecGrantStmt_oids(InternalGrant *istmt) + case ACL_OBJECT_LANGUAGE: + ExecGrant_Language(istmt); + break; ++ case ACL_OBJECT_LARGEOBJECT: ++ ExecGrant_Largeobject(istmt); ++ break; + case ACL_OBJECT_NAMESPACE: + ExecGrant_Namespace(istmt); + break; +@@ -533,6 +546,20 @@ objectNamesToOids(GrantObjectType objtype, List *objnames) + ReleaseSysCache(tuple); + } + break; ++ case ACL_OBJECT_LARGEOBJECT: ++ foreach(cell, objnames) ++ { ++ Oid lobjOid = intVal(lfirst(cell)); ++ ++ if (!LargeObjectExists(lobjOid)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("large object %u does not exist", ++ lobjOid))); ++ ++ objects = lappend_oid(objects, lobjOid); ++ } ++ break; + case ACL_OBJECT_NAMESPACE: + foreach(cell, objnames) + { +@@ -1746,6 +1773,138 @@ ExecGrant_Language(InternalGrant *istmt) + } + + static void ++ExecGrant_Largeobject(InternalGrant *istmt) ++{ ++ Relation relation; ++ ListCell *cell; ++ ++ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) ++ istmt->privileges = ACL_ALL_RIGHTS_LARGEOBJECT; ++ ++ relation = heap_open(LargeObjectMetadataRelationId, ++ RowExclusiveLock); ++ ++ foreach(cell, istmt->objects) ++ { ++ Oid loid = lfirst_oid(cell); ++ Form_pg_largeobject_metadata form_lo_meta; ++ char loname[NAMEDATALEN]; ++ Datum aclDatum; ++ bool isNull; ++ AclMode avail_goptions; ++ AclMode this_privileges; ++ Acl *old_acl; ++ Acl *new_acl; ++ Oid grantorId; ++ Oid ownerId; ++ HeapTuple newtuple; ++ Datum values[Natts_pg_largeobject_metadata]; ++ bool nulls[Natts_pg_largeobject_metadata]; ++ bool replaces[Natts_pg_largeobject_metadata]; ++ int noldmembers; ++ int nnewmembers; ++ Oid *oldmembers; ++ Oid *newmembers; ++ ScanKeyData entry[1]; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ ++ /* There's no syscache for pg_largeobject_metadata */ ++ ScanKeyInit(&entry[0], ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(loid)); ++ ++ scan = systable_beginscan(relation, ++ LargeObjectMetadataOidIndexId, true, ++ SnapshotNow, 1, entry); ++ ++ tuple = systable_getnext(scan); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for large object %u", loid); ++ ++ form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(tuple); ++ ++ /* ++ * Get owner ID and working copy of existing ACL. If there's no ACL, ++ * substitute the proper default. ++ */ ++ ownerId = form_lo_meta->lomowner; ++ aclDatum = heap_getattr(tuple, ++ Anum_pg_largeobject_metadata_lomacl, ++ RelationGetDescr(relation), &isNull); ++ if (isNull) ++ old_acl = acldefault(ACL_OBJECT_LARGEOBJECT, ownerId); ++ else ++ old_acl = DatumGetAclPCopy(aclDatum); ++ ++ /* Determine ID to do the grant as, and available grant options */ ++ select_best_grantor(GetUserId(), istmt->privileges, ++ old_acl, ownerId, ++ &grantorId, &avail_goptions); ++ ++ /* ++ * Restrict the privileges to what we can actually grant, and emit the ++ * standards-mandated warning and error messages. ++ */ ++ snprintf(loname, sizeof(loname), "large object %u", loid); ++ this_privileges = ++ restrict_and_check_grant(istmt->is_grant, avail_goptions, ++ istmt->all_privs, istmt->privileges, ++ loid, grantorId, ACL_KIND_LARGEOBJECT, ++ loname, 0, NULL); ++ ++ /* ++ * Generate new ACL. ++ * ++ * We need the members of both old and new ACLs so we can correct the ++ * shared dependency information. ++ */ ++ noldmembers = aclmembers(old_acl, &oldmembers); ++ ++ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, ++ istmt->grant_option, istmt->behavior, ++ istmt->grantees, this_privileges, ++ grantorId, ownerId); ++ ++ nnewmembers = aclmembers(new_acl, &newmembers); ++ ++ /* finished building new ACL value, now insert it */ ++ MemSet(values, 0, sizeof(values)); ++ MemSet(nulls, false, sizeof(nulls)); ++ MemSet(replaces, false, sizeof(replaces)); ++ ++ replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true; ++ values[Anum_pg_largeobject_metadata_lomacl - 1] ++ = PointerGetDatum(new_acl); ++ ++ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), ++ values, nulls, replaces); ++ ++ simple_heap_update(relation, &newtuple->t_self, newtuple); ++ ++ /* keep the catalog indexes up to date */ ++ CatalogUpdateIndexes(relation, newtuple); ++ ++ /* Update the shared dependency ACL info */ ++ updateAclDependencies(LargeObjectRelationId, ++ HeapTupleGetOid(tuple), 0, ++ ownerId, istmt->is_grant, ++ noldmembers, oldmembers, ++ nnewmembers, newmembers); ++ ++ systable_endscan(scan); ++ ++ pfree(new_acl); ++ ++ /* prevent error when processing duplicate objects */ ++ CommandCounterIncrement(); ++ } ++ ++ heap_close(relation, RowExclusiveLock); ++} ++ ++static void + ExecGrant_Namespace(InternalGrant *istmt) + { + Relation relation; +@@ -2085,6 +2244,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] = + gettext_noop("permission denied for type %s"), + /* ACL_KIND_LANGUAGE */ + gettext_noop("permission denied for language %s"), ++ /* ACL_KIND_LARGEOBJECT */ ++ gettext_noop("permission denied for large object %s"), + /* ACL_KIND_NAMESPACE */ + gettext_noop("permission denied for schema %s"), + /* ACL_KIND_OPCLASS */ +@@ -2123,6 +2284,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] = + gettext_noop("must be owner of type %s"), + /* ACL_KIND_LANGUAGE */ + gettext_noop("must be owner of language %s"), ++ /* ACL_KIND_LARGEOBJECT */ ++ gettext_noop("must be owner of large object %s"), + /* ACL_KIND_NAMESPACE */ + gettext_noop("must be owner of schema %s"), + /* ACL_KIND_OPCLASS */ +@@ -2242,6 +2405,9 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid, + return pg_proc_aclmask(table_oid, roleid, mask, how); + case ACL_KIND_LANGUAGE: + return pg_language_aclmask(table_oid, roleid, mask, how); ++ case ACL_KIND_LARGEOBJECT: ++ return pg_largeobject_aclmask_snapshot(table_oid, roleid, ++ mask, how, SnapshotNow); + case ACL_KIND_NAMESPACE: + return pg_namespace_aclmask(table_oid, roleid, mask, how); + case ACL_KIND_TABLESPACE: +@@ -2625,6 +2791,90 @@ pg_language_aclmask(Oid lang_oid, Oid roleid, + } + + /* ++ * Exported routine for examining a user's privileges for a largeobject ++ * ++ * The reason why this interface has an argument of snapshot is that ++ * we apply a snapshot available on lo_open(), not SnapshotNow, when ++ * it is opened as read-only mode. ++ * If we could see the metadata and data from inconsistent viewpoint, ++ * it will give us much confusion. So, we need to provide an interface ++ * which takes an argument of snapshot. ++ * ++ * If the caller refers a large object with a certain snapshot except ++ * for SnapshotNow, its permission checks should be also applied in ++ * the same snapshot. ++ */ ++AclMode ++pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid, ++ AclMode mask, AclMaskHow how, ++ Snapshot snapshot) ++{ ++ AclMode result; ++ Relation pg_lo_meta; ++ ScanKeyData entry[1]; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ Datum aclDatum; ++ bool isNull; ++ Acl *acl; ++ Oid ownerId; ++ ++ /* Superusers bypass all permission checking. */ ++ if (superuser_arg(roleid)) ++ return mask; ++ ++ /* ++ * Get the largeobject's ACL from pg_language_metadata ++ */ ++ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, ++ AccessShareLock); ++ ++ ScanKeyInit(&entry[0], ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(lobj_oid)); ++ ++ scan = systable_beginscan(pg_lo_meta, ++ LargeObjectMetadataOidIndexId, true, ++ snapshot, 1, entry); ++ ++ tuple = systable_getnext(scan); ++ if (!HeapTupleIsValid(tuple)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("large object %u does not exist", lobj_oid))); ++ ++ ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner; ++ ++ aclDatum = heap_getattr(tuple, Anum_pg_largeobject_metadata_lomacl, ++ RelationGetDescr(pg_lo_meta), &isNull); ++ ++ if (isNull) ++ { ++ /* No ACL, so build default ACL */ ++ acl = acldefault(ACL_OBJECT_LARGEOBJECT, ownerId); ++ aclDatum = (Datum) 0; ++ } ++ else ++ { ++ /* detoast ACL if necessary */ ++ acl = DatumGetAclP(aclDatum); ++ } ++ ++ result = aclmask(acl, roleid, ownerId, mask, how); ++ ++ /* if we have a detoasted copy, free it */ ++ if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) ++ pfree(acl); ++ ++ systable_endscan(scan); ++ ++ heap_close(pg_lo_meta, AccessShareLock); ++ ++ return result; ++} ++ ++/* + * Exported routine for examining a user's privileges for a namespace + */ + AclMode +@@ -3075,6 +3325,20 @@ pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode) + } + + /* ++ * Exported routine for checking a user's access privileges to a largeobject ++ */ ++AclResult ++pg_largeobject_aclcheck_snapshot(Oid lobj_oid, Oid roleid, AclMode mode, ++ Snapshot snapshot) ++{ ++ if (pg_largeobject_aclmask_snapshot(lobj_oid, roleid, mode, ++ ACLMASK_ANY, snapshot) != 0) ++ return ACLCHECK_OK; ++ else ++ return ACLCHECK_NO_PRIV; ++} ++ ++/* + * Exported routine for checking a user's access privileges to a namespace + */ + AclResult +@@ -3265,6 +3529,53 @@ pg_language_ownercheck(Oid lan_oid, Oid roleid) + } + + /* ++ * Ownership check for a largeobject (specified by OID) ++ * ++ * Note that we have no candidate to call this routine with a certain ++ * snapshot except for SnapshotNow, so we don't provide an interface ++ * with _snapshot() version now. ++ */ ++bool ++pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid) ++{ ++ Relation pg_lo_meta; ++ ScanKeyData entry[1]; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ Oid ownerId; ++ ++ /* Superusers bypass all permission checking. */ ++ if (superuser_arg(roleid)) ++ return true; ++ ++ /* There's no syscache for pg_largeobject_metadata */ ++ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, ++ AccessShareLock); ++ ++ ScanKeyInit(&entry[0], ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(lobj_oid)); ++ ++ scan = systable_beginscan(pg_lo_meta, ++ LargeObjectMetadataOidIndexId, true, ++ SnapshotNow, 1, entry); ++ ++ tuple = systable_getnext(scan); ++ if (!HeapTupleIsValid(tuple)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("large object %u does not exist", lobj_oid))); ++ ++ ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner; ++ ++ systable_endscan(scan); ++ heap_close(pg_lo_meta, AccessShareLock); ++ ++ return has_privs_of_role(roleid, ownerId); ++} ++ ++/* + * Ownership check for a namespace (specified by OID). + */ + bool +diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c +index cb9a9c2..416c149 100644 +--- a/src/backend/catalog/dependency.c ++++ b/src/backend/catalog/dependency.c +@@ -36,6 +36,7 @@ + #include "catalog/pg_foreign_data_wrapper.h" + #include "catalog/pg_foreign_server.h" + #include "catalog/pg_language.h" ++#include "catalog/pg_largeobject.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_opclass.h" + #include "catalog/pg_operator.h" +@@ -129,6 +130,7 @@ static const Oid object_classes[MAX_OCLASS] = { + ConversionRelationId, /* OCLASS_CONVERSION */ + AttrDefaultRelationId, /* OCLASS_DEFAULT */ + LanguageRelationId, /* OCLASS_LANGUAGE */ ++ LargeObjectRelationId, /* OCLASS_LARGEOBJECT */ + OperatorRelationId, /* OCLASS_OPERATOR */ + OperatorClassRelationId, /* OCLASS_OPCLASS */ + OperatorFamilyRelationId, /* OCLASS_OPFAMILY */ +@@ -1071,6 +1073,10 @@ doDeletion(const ObjectAddress *object) + DropProceduralLanguageById(object->objectId); + break; + ++ case OCLASS_LARGEOBJECT: ++ LargeObjectDrop(object->objectId); ++ break; ++ + case OCLASS_OPERATOR: + RemoveOperatorById(object->objectId); + break; +@@ -1984,6 +1990,10 @@ getObjectClass(const ObjectAddress *object) + Assert(object->objectSubId == 0); + return OCLASS_LANGUAGE; + ++ case LargeObjectRelationId: ++ Assert(object->objectSubId == 0); ++ return OCLASS_LARGEOBJECT; ++ + case OperatorRelationId: + Assert(object->objectSubId == 0); + return OCLASS_OPERATOR; +@@ -2232,6 +2242,10 @@ getObjectDescription(const ObjectAddress *object) + ReleaseSysCache(langTup); + break; + } ++ case OCLASS_LARGEOBJECT: ++ appendStringInfo(&buffer, _("large object %u"), ++ object->objectId); ++ break; + + case OCLASS_OPERATOR: + appendStringInfo(&buffer, _("operator %s"), +diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c +index c92ab02..54d992f 100644 +--- a/src/backend/catalog/pg_largeobject.c ++++ b/src/backend/catalog/pg_largeobject.c +@@ -16,8 +16,16 @@ + + #include "access/genam.h" + #include "access/heapam.h" ++#include "access/sysattr.h" ++#include "catalog/catalog.h" ++#include "catalog/dependency.h" + #include "catalog/indexing.h" ++#include "catalog/pg_authid.h" + #include "catalog/pg_largeobject.h" ++#include "catalog/pg_largeobject_metadata.h" ++#include "catalog/toasting.h" ++#include "miscadmin.h" ++#include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" + #include "utils/rel.h" +@@ -27,113 +35,258 @@ + /* + * Create a large object having the given LO identifier. + * +- * We do this by inserting an empty first page, so that the object will +- * appear to exist with size 0. Note that the unique index will reject +- * an attempt to create a duplicate page. ++ * We create a new large object by inserting an entry into ++ * pg_largeobject_metadata without any data pages, so that the object ++ * will appear to exist with size 0. + */ +-void ++Oid + LargeObjectCreate(Oid loid) + { +- Relation pg_largeobject; ++ Relation pg_lo_meta; + HeapTuple ntup; +- Datum values[Natts_pg_largeobject]; +- bool nulls[Natts_pg_largeobject]; +- int i; ++ Oid loid_new; ++ Datum values[Natts_pg_largeobject_metadata]; ++ bool nulls[Natts_pg_largeobject_metadata]; + +- pg_largeobject = heap_open(LargeObjectRelationId, RowExclusiveLock); ++ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, ++ RowExclusiveLock); + + /* +- * Form new tuple ++ * Insert metadata of the largeobject + */ +- for (i = 0; i < Natts_pg_largeobject; i++) +- { +- values[i] = (Datum) NULL; +- nulls[i] = false; +- } ++ memset(values, 0, sizeof(values)); ++ memset(nulls, false, sizeof(nulls)); + +- i = 0; +- values[i++] = ObjectIdGetDatum(loid); +- values[i++] = Int32GetDatum(0); +- values[i++] = DirectFunctionCall1(byteain, +- CStringGetDatum("")); ++ values[Anum_pg_largeobject_metadata_lomowner - 1] ++ = ObjectIdGetDatum(GetUserId()); ++ nulls[Anum_pg_largeobject_metadata_lomacl - 1] = true; + +- ntup = heap_form_tuple(pg_largeobject->rd_att, values, nulls); ++ ntup = heap_form_tuple(RelationGetDescr(pg_lo_meta), ++ values, nulls); ++ if (OidIsValid(loid)) ++ HeapTupleSetOid(ntup, loid); + +- /* +- * Insert it +- */ +- simple_heap_insert(pg_largeobject, ntup); +- +- /* Update indexes */ +- CatalogUpdateIndexes(pg_largeobject, ntup); ++ loid_new = simple_heap_insert(pg_lo_meta, ntup); ++ Assert(!OidIsValid(loid) || loid == loid_new); + +- heap_close(pg_largeobject, RowExclusiveLock); ++ CatalogUpdateIndexes(pg_lo_meta, ntup); + + heap_freetuple(ntup); ++ ++ heap_close(pg_lo_meta, RowExclusiveLock); ++ ++ return loid_new; + } + ++/* ++ * Drop a large object having the given LO identifier. ++ * ++ * When we drop a large object, it is necessary to drop both of metadata ++ * and data pages in same time. ++ */ + void + LargeObjectDrop(Oid loid) + { +- bool found = false; ++ Relation pg_lo_meta; + Relation pg_largeobject; + ScanKeyData skey[1]; +- SysScanDesc sd; ++ SysScanDesc scan; + HeapTuple tuple; + ++ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, ++ RowExclusiveLock); ++ ++ pg_largeobject = heap_open(LargeObjectRelationId, ++ RowExclusiveLock); ++ ++ /* ++ * Delete an entry from pg_largeobject_metadata ++ */ + ScanKeyInit(&skey[0], +- Anum_pg_largeobject_loid, ++ ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, +- ObjectIdGetDatum(loid)); ++ ObjectIdGetDatum(loid)); + +- pg_largeobject = heap_open(LargeObjectRelationId, RowExclusiveLock); ++ scan = systable_beginscan(pg_lo_meta, ++ LargeObjectMetadataOidIndexId, true, ++ SnapshotNow, 1, skey); + +- sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, +- SnapshotNow, 1, skey); ++ tuple = systable_getnext(scan); ++ if (!HeapTupleIsValid(tuple)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("large object %u does not exist", loid))); ++ ++ simple_heap_delete(pg_lo_meta, &tuple->t_self); ++ ++ systable_endscan(scan); ++ ++ /* ++ * Delete all the associated entries from pg_largeobject ++ */ ++ ScanKeyInit(&skey[0], ++ Anum_pg_largeobject_loid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(loid)); + +- while ((tuple = systable_getnext(sd)) != NULL) ++ scan = systable_beginscan(pg_largeobject, ++ LargeObjectLOidPNIndexId, true, ++ SnapshotNow, 1, skey); ++ while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + simple_heap_delete(pg_largeobject, &tuple->t_self); +- found = true; + } + +- systable_endscan(sd); ++ systable_endscan(scan); + + heap_close(pg_largeobject, RowExclusiveLock); + +- if (!found) ++ heap_close(pg_lo_meta, RowExclusiveLock); ++} ++ ++/* ++ * LargeObjectAlterOwner ++ * ++ * Implementation of ALTER LARGE OBJECT statement ++ */ ++void ++LargeObjectAlterOwner(Oid loid, Oid newOwnerId) ++{ ++ Form_pg_largeobject_metadata form_lo_meta; ++ Relation pg_lo_meta; ++ ScanKeyData skey[1]; ++ SysScanDesc scan; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ ++ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, ++ RowExclusiveLock); ++ ++ ScanKeyInit(&skey[0], ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(loid)); ++ ++ scan = systable_beginscan(pg_lo_meta, ++ LargeObjectMetadataOidIndexId, true, ++ SnapshotNow, 1, skey); ++ ++ oldtup = systable_getnext(scan); ++ if (!HeapTupleIsValid(oldtup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("large object %u does not exist", loid))); ++ ++ form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(oldtup); ++ if (form_lo_meta->lomowner != newOwnerId) ++ { ++ Datum values[Natts_pg_largeobject_metadata]; ++ bool nulls[Natts_pg_largeobject_metadata]; ++ bool replaces[Natts_pg_largeobject_metadata]; ++ Acl *newAcl; ++ Datum aclDatum; ++ bool isnull; ++ ++ /* Superusers can always do it */ ++ if (!superuser()) ++ { ++ /* ++ * The 'lo_compat_privileges' is not checked here, because we ++ * don't have any access control features in the 8.4.x series ++ * or earlier release. ++ * So, it is not a place we can define a compatible behavior. ++ */ ++ ++ /* Otherwise, must be owner of the existing object */ ++ if (!pg_largeobject_ownercheck(loid, GetUserId())) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("must be owner of large object %u", loid))); ++ ++ /* Must be able to become new owner */ ++ check_is_member_of_role(GetUserId(), newOwnerId); ++ } ++ ++ memset(values, 0, sizeof(values)); ++ memset(nulls, false, sizeof(nulls)); ++ memset(replaces, false, sizeof(nulls)); ++ ++ values[Anum_pg_largeobject_metadata_lomowner - 1] ++ = ObjectIdGetDatum(newOwnerId); ++ replaces[Anum_pg_largeobject_metadata_lomowner - 1] = true; ++ ++ /* ++ * Determine the modified ACL for the new owner. ++ * This is only necessary when the ACL is non-null. ++ */ ++ aclDatum = heap_getattr(oldtup, ++ Anum_pg_largeobject_metadata_lomacl, ++ RelationGetDescr(pg_lo_meta), &isnull); ++ if (!isnull) ++ { ++ newAcl = aclnewowner(DatumGetAclP(aclDatum), ++ form_lo_meta->lomowner, newOwnerId); ++ values[Anum_pg_largeobject_metadata_lomacl - 1] ++ = PointerGetDatum(newAcl); ++ replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true; ++ } ++ ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_lo_meta), ++ values, nulls, replaces); ++ ++ simple_heap_update(pg_lo_meta, &newtup->t_self, newtup); ++ CatalogUpdateIndexes(pg_lo_meta, newtup); ++ ++ heap_freetuple(newtup); ++ ++ /* Update owner dependency reference */ ++ changeDependencyOnOwner(LargeObjectRelationId, ++ loid, newOwnerId); ++ } ++ systable_endscan(scan); ++ ++ heap_close(pg_lo_meta, RowExclusiveLock); + } + ++/* ++ * LargeObjectExists ++ * ++ * Currently, we don't use system cache to contain metadata of ++ * large objects, because massive number of large objects can ++ * consume not a small amount of process local memory. ++ * ++ * Note that LargeObjectExists always scans the system catalog ++ * with SnapshotNow, so it is unavailable to use to check ++ * existence in read-only accesses. ++ */ + bool + LargeObjectExists(Oid loid) + { ++ Relation pg_lo_meta; ++ ScanKeyData skey[1]; ++ SysScanDesc sd; ++ HeapTuple tuple; + bool retval = false; +- Relation pg_largeobject; +- ScanKeyData skey[1]; +- SysScanDesc sd; + +- /* +- * See if we can find any tuples belonging to the specified LO +- */ + ScanKeyInit(&skey[0], +- Anum_pg_largeobject_loid, ++ ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(loid)); + +- pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock); ++ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, ++ AccessShareLock); + +- sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, ++ sd = systable_beginscan(pg_lo_meta, ++ LargeObjectMetadataOidIndexId, true, + SnapshotNow, 1, skey); + +- if (systable_getnext(sd) != NULL) ++ tuple = systable_getnext(sd); ++ if (HeapTupleIsValid(tuple)) + retval = true; + + systable_endscan(sd); + +- heap_close(pg_largeobject, AccessShareLock); ++ heap_close(pg_lo_meta, AccessShareLock); + + return retval; + } +diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c +index cd04053..451724f 100644 +--- a/src/backend/catalog/pg_shdepend.c ++++ b/src/backend/catalog/pg_shdepend.c +@@ -24,6 +24,7 @@ + #include "catalog/pg_conversion.h" + #include "catalog/pg_database.h" + #include "catalog/pg_language.h" ++#include "catalog/pg_largeobject.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_operator.h" + #include "catalog/pg_proc.h" +@@ -1210,6 +1211,9 @@ shdepDropOwned(List *roleids, DropBehavior behavior) + case LanguageRelationId: + istmt.objtype = ACL_OBJECT_LANGUAGE; + break; ++ case LargeObjectRelationId: ++ istmt.objtype = ACL_OBJECT_LARGEOBJECT; ++ break; + case NamespaceRelationId: + istmt.objtype = ACL_OBJECT_NAMESPACE; + break; +@@ -1365,6 +1369,10 @@ shdepReassignOwned(List *roleids, Oid newrole) + AlterLanguageOwner_oid(sdepForm->objid, newrole); + break; + ++ case LargeObjectRelationId: ++ LargeObjectAlterOwner(sdepForm->objid, newrole); ++ break; ++ + default: + elog(ERROR, "unexpected classid %d", sdepForm->classid); + break; +diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c +index 835b738..46bc4df 100644 +--- a/src/backend/commands/alter.c ++++ b/src/backend/commands/alter.c +@@ -15,6 +15,7 @@ + #include "postgres.h" + + #include "catalog/namespace.h" ++#include "catalog/pg_largeobject.h" + #include "commands/alter.h" + #include "commands/conversioncmds.h" + #include "commands/dbcommands.h" +@@ -233,6 +234,10 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) + AlterLanguageOwner(strVal(linitial(stmt->object)), newowner); + break; + ++ case OBJECT_LARGEOBJECT: ++ LargeObjectAlterOwner(intVal(linitial(stmt->object)), newowner); ++ break; ++ + case OBJECT_OPERATOR: + Assert(list_length(stmt->objarg) == 2); + AlterOperatorOwner(stmt->object, +diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c +index ccf33ea..aaaf09a 100644 +--- a/src/backend/commands/comment.c ++++ b/src/backend/commands/comment.c +@@ -25,6 +25,7 @@ + #include "catalog/pg_description.h" + #include "catalog/pg_language.h" + #include "catalog/pg_largeobject.h" ++#include "catalog/pg_largeobject_metadata.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_opclass.h" + #include "catalog/pg_operator.h" +@@ -42,6 +43,7 @@ + #include "commands/comment.h" + #include "commands/dbcommands.h" + #include "commands/tablespace.h" ++#include "libpq/be-fsstubs.h" + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" +@@ -1422,7 +1424,20 @@ CommentLargeObject(List *qualname, char *comment) + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("large object %u does not exist", loid))); + +- /* Call CreateComments() to create/drop the comments */ ++ /* Permission checks */ ++ if (!lo_compat_privileges && ++ !pg_largeobject_ownercheck(loid, GetUserId())) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("must be owner of large object %u", loid))); ++ ++ /* ++ * Call CreateComments() to create/drop the comments ++ * ++ * See the comment in the inv_create() which describes ++ * the reason why LargeObjectRelationId is used instead ++ * of the LargeObjectMetadataRelationId. ++ */ + CreateComments(loid, LargeObjectRelationId, 0, comment); + } + +diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c +index 24b1223..96dda00 100644 +--- a/src/backend/commands/tablecmds.c ++++ b/src/backend/commands/tablecmds.c +@@ -5902,6 +5902,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, + case OCLASS_CAST: + case OCLASS_CONVERSION: + case OCLASS_LANGUAGE: ++ case OCLASS_LARGEOBJECT: + case OCLASS_OPERATOR: + case OCLASS_OPCLASS: + case OCLASS_OPFAMILY: +diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c +index 0831071..b6c46c1 100644 +--- a/src/backend/libpq/be-fsstubs.c ++++ b/src/backend/libpq/be-fsstubs.c +@@ -42,14 +42,20 @@ + #include + #include + ++#include "catalog/pg_largeobject_metadata.h" + #include "libpq/be-fsstubs.h" + #include "libpq/libpq-fs.h" + #include "miscadmin.h" + #include "storage/fd.h" + #include "storage/large_object.h" ++#include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/memutils.h" + ++/* ++ * compatibility flag for permission checks ++ */ ++bool lo_compat_privileges; + + /*#define FSDB 1*/ + #define BUFSIZE 8192 +@@ -156,6 +162,17 @@ lo_read(int fd, char *buf, int len) + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("invalid large-object descriptor: %d", fd))); + ++ /* Permission checks */ ++ if (!lo_compat_privileges && ++ pg_largeobject_aclcheck_snapshot(cookies[fd]->id, ++ GetUserId(), ++ ACL_SELECT, ++ cookies[fd]->snapshot) != ACLCHECK_OK) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("permission denied for large object %u", ++ cookies[fd]->id))); ++ + status = inv_read(cookies[fd], buf, len); + + return status; +@@ -177,6 +194,17 @@ lo_write(int fd, const char *buf, int len) + errmsg("large object descriptor %d was not opened for writing", + fd))); + ++ /* Permission checks */ ++ if (!lo_compat_privileges && ++ pg_largeobject_aclcheck_snapshot(cookies[fd]->id, ++ GetUserId(), ++ ACL_UPDATE, ++ cookies[fd]->snapshot) != ACLCHECK_OK) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("permission denied for large object %u", ++ cookies[fd]->id))); ++ + status = inv_write(cookies[fd], buf, len); + + return status; +@@ -251,6 +279,13 @@ lo_unlink(PG_FUNCTION_ARGS) + { + Oid lobjId = PG_GETARG_OID(0); + ++ /* Must be owner of the largeobject */ ++ if (!lo_compat_privileges && ++ !pg_largeobject_ownercheck(lobjId, GetUserId())) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("must be owner of large object %u", lobjId))); ++ + /* + * If there are any open LO FDs referencing that ID, close 'em. + */ +@@ -482,6 +517,17 @@ lo_truncate(PG_FUNCTION_ARGS) + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("invalid large-object descriptor: %d", fd))); + ++ /* Permission checks */ ++ if (!lo_compat_privileges && ++ pg_largeobject_aclcheck_snapshot(cookies[fd]->id, ++ GetUserId(), ++ ACL_UPDATE, ++ cookies[fd]->snapshot) != ACLCHECK_OK) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("permission denied for large object %u", ++ cookies[fd]->id))); ++ + inv_truncate(cookies[fd], len); + + PG_RETURN_INT32(0); +diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y +index 20ab0ba..d13b0f0 100644 +--- a/src/backend/parser/gram.y ++++ b/src/backend/parser/gram.y +@@ -378,6 +378,7 @@ static TypeName *TableFuncTypeName(List *columns); + %type opt_varying opt_timezone + + %type Iconst SignedIconst ++%type Iconst_list + %type Sconst comment_text + %type RoleId opt_granted_by opt_boolean ColId_or_Sconst + %type var_list +@@ -4379,6 +4380,13 @@ privilege_target: + n->objs = $2; + $$ = n; + } ++ | LARGE_P OBJECT_P Iconst_list ++ { ++ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); ++ n->objtype = ACL_OBJECT_LARGEOBJECT; ++ n->objs = $3; ++ $$ = n; ++ } + | SCHEMA name_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); +@@ -5506,6 +5514,14 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId + n->newowner = $7; + $$ = (Node *)n; + } ++ | ALTER LARGE_P OBJECT_P Iconst OWNER TO RoleId ++ { ++ AlterOwnerStmt *n = makeNode(AlterOwnerStmt); ++ n->objectType = OBJECT_LARGEOBJECT; ++ n->object = list_make1(makeInteger($4)); ++ n->newowner = $7; ++ $$ = (Node *)n; ++ } + | ALTER OPERATOR any_operator oper_argtypes OWNER TO RoleId + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); +@@ -10066,6 +10082,10 @@ SignedIconst: Iconst { $$ = $1; } + | '-' Iconst { $$ = - $2; } + ; + ++Iconst_list: Iconst { $$ = list_make1(makeInteger($1)); } ++ | Iconst_list ',' Iconst { $$ = lappend($1, makeInteger($3)); } ++ ; ++ + /* + * Name classification hierarchy. + * +diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c +index 51b49dd..a946972 100644 +--- a/src/backend/storage/large_object/inv_api.c ++++ b/src/backend/storage/large_object/inv_api.c +@@ -32,18 +32,23 @@ + + #include "access/genam.h" + #include "access/heapam.h" ++#include "access/sysattr.h" + #include "access/tuptoaster.h" + #include "access/xact.h" + #include "catalog/catalog.h" ++#include "catalog/dependency.h" + #include "catalog/indexing.h" + #include "catalog/pg_largeobject.h" ++#include "catalog/pg_largeobject_metadata.h" + #include "commands/comment.h" + #include "libpq/libpq-fs.h" ++#include "miscadmin.h" + #include "storage/large_object.h" + #include "utils/fmgroids.h" + #include "utils/rel.h" + #include "utils/resowner.h" + #include "utils/snapmgr.h" ++#include "utils/syscache.h" + #include "utils/tqual.h" + + +@@ -139,30 +144,31 @@ close_lo_relation(bool isCommit) + static bool + myLargeObjectExists(Oid loid, Snapshot snapshot) + { ++ Relation pg_lo_meta; ++ ScanKeyData skey[1]; ++ SysScanDesc sd; ++ HeapTuple tuple; + bool retval = false; +- Relation pg_largeobject; +- ScanKeyData skey[1]; +- SysScanDesc sd; + +- /* +- * See if we can find any tuples belonging to the specified LO +- */ + ScanKeyInit(&skey[0], +- Anum_pg_largeobject_loid, ++ ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(loid)); + +- pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock); ++ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, ++ AccessShareLock); + +- sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, ++ sd = systable_beginscan(pg_lo_meta, ++ LargeObjectMetadataOidIndexId, true, + snapshot, 1, skey); + +- if (systable_getnext(sd) != NULL) ++ tuple = systable_getnext(sd); ++ if (HeapTupleIsValid(tuple)) + retval = true; + + systable_endscan(sd); + +- heap_close(pg_largeobject, AccessShareLock); ++ heap_close(pg_lo_meta, AccessShareLock); + + return retval; + } +@@ -193,31 +199,31 @@ getbytealen(bytea *data) + Oid + inv_create(Oid lobjId) + { ++ Oid lobjId_new; ++ + /* +- * Allocate an OID to be the LO's identifier, unless we were told what to +- * use. We can use the index on pg_largeobject for checking OID +- * uniqueness, even though it has additional columns besides OID. ++ * Create a new largeobject with empty data pages + */ +- if (!OidIsValid(lobjId)) +- { +- open_lo_relation(); +- +- lobjId = GetNewOidWithIndex(lo_heap_r, LargeObjectLOidPNIndexId, +- Anum_pg_largeobject_loid); +- } ++ lobjId_new = LargeObjectCreate(lobjId); + + /* +- * Create the LO by writing an empty first page for it in pg_largeobject +- * (will fail if duplicate) ++ * dependency on the owner of largeobject ++ * ++ * The reason why we use LargeObjectRelationId instead of ++ * LargeObjectMetadataRelationId here is to provide backward ++ * compatibility to the applications which utilize a knowledge ++ * about internal layout of system catalogs. ++ * OID of pg_largeobject_metadata and loid of pg_largeobject ++ * are same value, so there are no actual differences here. + */ +- LargeObjectCreate(lobjId); +- ++ recordDependencyOnOwner(LargeObjectRelationId, ++ lobjId_new, GetUserId()); + /* + * Advance command counter to make new tuple visible to later operations. + */ + CommandCounterIncrement(); + +- return lobjId; ++ return lobjId_new; + } + + /* +@@ -292,10 +298,15 @@ inv_close(LargeObjectDesc *obj_desc) + int + inv_drop(Oid lobjId) + { +- LargeObjectDrop(lobjId); ++ ObjectAddress object; + +- /* Delete any comments on the large object */ +- DeleteComments(lobjId, LargeObjectRelationId, 0); ++ /* ++ * Delete any comments and dependencies on the large object ++ */ ++ object.classId = LargeObjectRelationId; ++ object.objectId = lobjId; ++ object.objectSubId = 0; ++ performDeletion(&object, DROP_CASCADE); + + /* + * Advance command counter so that tuple removal will be seen by later +@@ -315,7 +326,6 @@ inv_drop(Oid lobjId) + static uint32 + inv_getsize(LargeObjectDesc *obj_desc) + { +- bool found = false; + uint32 lastbyte = 0; + ScanKeyData skey[1]; + SysScanDesc sd; +@@ -339,13 +349,13 @@ inv_getsize(LargeObjectDesc *obj_desc) + * large object in reverse pageno order. So, it's sufficient to examine + * the first valid tuple (== last valid page). + */ +- while ((tuple = systable_getnext_ordered(sd, BackwardScanDirection)) != NULL) ++ tuple = systable_getnext_ordered(sd, BackwardScanDirection); ++ if (HeapTupleIsValid(tuple)) + { + Form_pg_largeobject data; + bytea *datafield; + bool pfreeit; + +- found = true; + if (HeapTupleHasNulls(tuple)) /* paranoia */ + elog(ERROR, "null field found in pg_largeobject"); + data = (Form_pg_largeobject) GETSTRUCT(tuple); +@@ -360,15 +370,10 @@ inv_getsize(LargeObjectDesc *obj_desc) + lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield); + if (pfreeit) + pfree(datafield); +- break; + } + + systable_endscan_ordered(sd); + +- if (!found) +- ereport(ERROR, +- (errcode(ERRCODE_UNDEFINED_OBJECT), +- errmsg("large object %u does not exist", obj_desc->id))); + return lastbyte; + } + +@@ -545,6 +550,12 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes) + errmsg("large object %u was not opened for writing", + obj_desc->id))); + ++ /* check existence of the target largeobject */ ++ if (!LargeObjectExists(obj_desc->id)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("large object %u was already dropped", obj_desc->id))); ++ + if (nbytes <= 0) + return 0; + +@@ -736,6 +747,12 @@ inv_truncate(LargeObjectDesc *obj_desc, int len) + errmsg("large object %u was not opened for writing", + obj_desc->id))); + ++ /* check existence of the target largeobject */ ++ if (!LargeObjectExists(obj_desc->id)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("large object %u was already dropped", obj_desc->id))); ++ + open_lo_relation(); + + indstate = CatalogOpenIndexes(lo_heap_r); +diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c +index 8adb79f..9e82a48 100644 +--- a/src/backend/tcop/utility.c ++++ b/src/backend/tcop/utility.c +@@ -1625,6 +1625,9 @@ CreateCommandTag(Node *parsetree) + case OBJECT_LANGUAGE: + tag = "ALTER LANGUAGE"; + break; ++ case OBJECT_LARGEOBJECT: ++ tag = "ALTER LARGE OBJECT"; ++ break; + case OBJECT_OPERATOR: + tag = "ALTER OPERATOR"; + break; +diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c +index 334823b..1de704a 100644 +--- a/src/backend/utils/adt/acl.c ++++ b/src/backend/utils/adt/acl.c +@@ -631,6 +631,11 @@ acldefault(GrantObjectType objtype, Oid ownerId) + world_default = ACL_USAGE; + owner_default = ACL_ALL_RIGHTS_LANGUAGE; + break; ++ case ACL_OBJECT_LARGEOBJECT: ++ /* Grant SELECT,UPDATE by default, for now */ ++ world_default = ACL_NO_RIGHTS; ++ owner_default = ACL_ALL_RIGHTS_LARGEOBJECT; ++ break; + case ACL_OBJECT_NAMESPACE: + world_default = ACL_NO_RIGHTS; + owner_default = ACL_ALL_RIGHTS_NAMESPACE; +diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c +index cb59d35..db1d933 100644 +--- a/src/backend/utils/misc/guc.c ++++ b/src/backend/utils/misc/guc.c +@@ -38,6 +38,7 @@ + #include "commands/trigger.h" + #include "funcapi.h" + #include "libpq/auth.h" ++#include "libpq/be-fsstubs.h" + #include "libpq/pqformat.h" + #include "miscadmin.h" + #include "optimizer/cost.h" +@@ -1221,6 +1222,16 @@ static struct config_bool ConfigureNamesBool[] = + false, NULL, NULL + }, + ++ { ++ {"lo_compat_privileges", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS, ++ gettext_noop("Enables backward compatibility in privilege checks on large objects"), ++ gettext_noop("When turned on, privilege checks on large objects perform " ++ "with backward compatibility as 8.4.x or earlier releases.") ++ }, ++ &lo_compat_privileges, ++ false, NULL, NULL ++ }, ++ + /* End-of-list marker */ + { + {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL +diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample +index 645f355..85acc4e 100644 +--- a/src/backend/utils/misc/postgresql.conf.sample ++++ b/src/backend/utils/misc/postgresql.conf.sample +@@ -483,6 +483,7 @@ + #backslash_quote = safe_encoding # on, off, or safe_encoding + #default_with_oids = off + #escape_string_warning = on ++#lo_compat_privileges = off + #regex_flavor = advanced # advanced, extended, or basic + #sql_inheritance = on + #standard_conforming_strings = off +diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c +index b14f3a0..1fcf590 100644 +--- a/src/bin/initdb/initdb.c ++++ b/src/bin/initdb/initdb.c +@@ -1815,6 +1815,7 @@ setup_privileges(void) + " WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;\n", + "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n", + "GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n", ++ "REVOKE ALL ON pg_largeobject FROM PUBLIC;\n", + NULL + }; + +diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c +index 93bd4d4..a14f4db 100644 +--- a/src/bin/pg_dump/dumputils.c ++++ b/src/bin/pg_dump/dumputils.c +@@ -758,6 +758,11 @@ do { \ + CONVERT_PRIV('U', "USAGE"); + else if (strcmp(type, "SERVER") == 0) + CONVERT_PRIV('U', "USAGE"); ++ else if (strcmp(type, "LARGE OBJECT") == 0) ++ { ++ CONVERT_PRIV('r', "SELECT"); ++ CONVERT_PRIV('w', "UPDATE"); ++ } + else + abort(); + +diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c +index c6a178f..57b5f7d 100644 +--- a/src/bin/pg_dump/pg_dump.c ++++ b/src/bin/pg_dump/pg_dump.c +@@ -1923,7 +1923,9 @@ hasBlobs(Archive *AH) + selectSourceSchema("pg_catalog"); + + /* Check for BLOB OIDs */ +- if (AH->remoteVersion >= 70100) ++ if (AH->remoteVersion >= 80402) ++ blobQry = "SELECT oid FROM pg_largeobject_metadata LIMIT 1"; ++ else if (AH->remoteVersion >= 70100) + blobQry = "SELECT loid FROM pg_largeobject LIMIT 1"; + else + blobQry = "SELECT oid FROM pg_class WHERE relkind = 'l' LIMIT 1"; +@@ -1959,7 +1961,9 @@ dumpBlobs(Archive *AH, void *arg) + selectSourceSchema("pg_catalog"); + + /* Cursor to get all BLOB OIDs */ +- if (AH->remoteVersion >= 70100) ++ if (AH->remoteVersion >= 80402) ++ blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_largeobject_metadata"; ++ else if (AH->remoteVersion >= 70100) + blobQry = "DECLARE bloboid CURSOR FOR SELECT DISTINCT loid FROM pg_largeobject"; + else + blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_class WHERE relkind = 'l'"; +@@ -2023,7 +2027,9 @@ dumpBlobs(Archive *AH, void *arg) + + /* + * dumpBlobComments +- * dump all blob comments ++ * dump all blob properties. ++ * It has "BLOB COMMENTS" tag due to the historical reason, but note ++ * that it is the routine to dump all the properties of blobs. + * + * Since we don't provide any way to be selective about dumping blobs, + * there's no need to be selective about their comments either. We put +@@ -2034,30 +2040,35 @@ dumpBlobComments(Archive *AH, void *arg) + { + const char *blobQry; + const char *blobFetchQry; +- PQExpBuffer commentcmd = createPQExpBuffer(); ++ PQExpBuffer cmdQry = createPQExpBuffer(); + PGresult *res; + int i; + + if (g_verbose) +- write_msg(NULL, "saving large object comments\n"); ++ write_msg(NULL, "saving large object properties\n"); + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + + /* Cursor to get all BLOB comments */ +- if (AH->remoteVersion >= 70300) ++ if (AH->remoteVersion >= 80402) ++ blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, " ++ "obj_description(oid, 'pg_largeobject'), " ++ "pg_get_userbyid(lomowner), lomacl " ++ "FROM pg_largeobject_metadata"; ++ else if (AH->remoteVersion >= 70300) + blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, " +- "obj_description(loid, 'pg_largeobject') " ++ "obj_description(loid, 'pg_largeobject'), NULL, NULL " + "FROM (SELECT DISTINCT loid FROM " + "pg_description d JOIN pg_largeobject l ON (objoid = loid) " + "WHERE classoid = 'pg_largeobject'::regclass) ss"; + else if (AH->remoteVersion >= 70200) + blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, " +- "obj_description(loid, 'pg_largeobject') " ++ "obj_description(loid, 'pg_largeobject'), NULL, NULL " + "FROM (SELECT DISTINCT loid FROM pg_largeobject) ss"; + else if (AH->remoteVersion >= 70100) + blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, " +- "obj_description(loid) " ++ "obj_description(loid), NULL, NULL " + "FROM (SELECT DISTINCT loid FROM pg_largeobject) ss"; + else + blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, " +@@ -2065,7 +2076,7 @@ dumpBlobComments(Archive *AH, void *arg) + " SELECT description " + " FROM pg_description pd " + " WHERE pd.objoid=pc.oid " +- " ) " ++ " ), NULL, NULL " + "FROM pg_class pc WHERE relkind = 'l'"; + + res = PQexec(g_conn, blobQry); +@@ -2085,22 +2096,51 @@ dumpBlobComments(Archive *AH, void *arg) + /* Process the tuples, if any */ + for (i = 0; i < PQntuples(res); i++) + { +- Oid blobOid; +- char *comment; ++ Oid blobOid = atooid(PQgetvalue(res, i, 0)); ++ char *lo_comment = PQgetvalue(res, i, 1); ++ char *lo_owner = PQgetvalue(res, i, 2); ++ char *lo_acl = PQgetvalue(res, i, 3); ++ char lo_name[32]; + +- /* ignore blobs without comments */ +- if (PQgetisnull(res, i, 1)) +- continue; ++ resetPQExpBuffer(cmdQry); + +- blobOid = atooid(PQgetvalue(res, i, 0)); +- comment = PQgetvalue(res, i, 1); ++ /* comment on the blob */ ++ if (!PQgetisnull(res, i, 1)) ++ { ++ appendPQExpBuffer(cmdQry, ++ "COMMENT ON LARGE OBJECT %u IS ", blobOid); ++ appendStringLiteralAH(cmdQry, lo_comment, AH); ++ appendPQExpBuffer(cmdQry, ";\n"); ++ } ++ ++ /* dump blob ownership, if necessary */ ++ if (!PQgetisnull(res, i, 2)) ++ { ++ appendPQExpBuffer(cmdQry, ++ "ALTER LARGE OBJECT %u OWNER TO %s;\n", ++ blobOid, lo_owner); ++ } + +- printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ", +- blobOid); +- appendStringLiteralAH(commentcmd, comment, AH); +- appendPQExpBuffer(commentcmd, ";\n"); ++ /* dump blob privileges, if necessary */ ++ if (!PQgetisnull(res, i, 3) && ++ !dataOnly && !aclsSkip) ++ { ++ snprintf(lo_name, sizeof(lo_name), "%u", blobOid); ++ if (!buildACLCommands(lo_name, NULL, "LARGE OBJECT", ++ lo_acl, lo_owner, ++ AH->remoteVersion, cmdQry)) ++ { ++ write_msg(NULL, "could not parse ACL (%s) for " ++ "large object %u", lo_acl, blobOid); ++ exit_nicely(); ++ } ++ } + +- archputs(commentcmd->data, AH); ++ if (cmdQry->len > 0) ++ { ++ appendPQExpBuffer(cmdQry, "\n"); ++ archputs(cmdQry->data, AH); ++ } + } + } while (PQntuples(res) > 0); + +@@ -2108,7 +2148,7 @@ dumpBlobComments(Archive *AH, void *arg) + + archputs("\n", AH); + +- destroyPQExpBuffer(commentcmd); ++ destroyPQExpBuffer(cmdQry); + + return 1; + } +diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c +index a97cc73..f829368 100644 +--- a/src/bin/psql/large_obj.c ++++ b/src/bin/psql/large_obj.c +@@ -278,13 +278,28 @@ do_lo_list(void) + char buf[1024]; + printQueryOpt myopt = pset.popt; + +- snprintf(buf, sizeof(buf), +- "SELECT loid as \"%s\",\n" +- " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n" +- "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n" +- "ORDER BY 1", +- gettext_noop("ID"), +- gettext_noop("Description")); ++ if (pset.sversion >= 80500) ++ { ++ snprintf(buf, sizeof(buf), ++ "SELECT oid as \"%s\",\n" ++ " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n" ++ " pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n" ++ " FROM pg_catalog.pg_largeobject_metadata " ++ " ORDER BY oid", ++ gettext_noop("ID"), ++ gettext_noop("Owner"), ++ gettext_noop("Description")); ++ } ++ else ++ { ++ snprintf(buf, sizeof(buf), ++ "SELECT loid as \"%s\",\n" ++ " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n" ++ "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n" ++ "ORDER BY 1", ++ gettext_noop("ID"), ++ gettext_noop("Description")); ++ } + + res = PSQLexec(buf, false); + if (!res) +diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c +index 6fef61b..6457c9c 100644 +--- a/src/bin/psql/tab-complete.c ++++ b/src/bin/psql/tab-complete.c +@@ -693,7 +693,7 @@ psql_completion(char *text, int start, int end) + { + static const char *const list_ALTER[] = + {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION", +- "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE", ++ "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE", + "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL}; + + COMPLETE_WITH_LIST(list_ALTER); +@@ -762,6 +762,17 @@ psql_completion(char *text, int start, int end) + COMPLETE_WITH_LIST(list_ALTERLANGUAGE); + } + ++ /* ALTER LARGE OBJECT */ ++ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && ++ pg_strcasecmp(prev3_wd, "LARGE") == 0 && ++ pg_strcasecmp(prev2_wd, "OBJECT") == 0) ++ { ++ static const char *const list_ALTERLARGEOBJECT[] = ++ {"OWNER TO", NULL}; ++ ++ COMPLETE_WITH_LIST(list_ALTERLARGEOBJECT); ++ } ++ + /* ALTER USER,ROLE */ + else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && + !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) && +@@ -1703,6 +1714,7 @@ psql_completion(char *text, int start, int end) + " UNION SELECT 'FOREIGN SERVER'" + " UNION SELECT 'FUNCTION'" + " UNION SELECT 'LANGUAGE'" ++ " UNION SELECT 'LARGE OBJECT'" + " UNION SELECT 'SCHEMA'" + " UNION SELECT 'TABLESPACE'"); + +diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h +index 1e74251..5459f12 100644 +--- a/src/include/catalog/catversion.h ++++ b/src/include/catalog/catversion.h +@@ -53,6 +53,6 @@ + */ + + /* yyyymmddN */ +-#define CATALOG_VERSION_NO 200904091 ++#define CATALOG_VERSION_NO 200912151 + + #endif +diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h +index a2f6761..62b9a52 100644 +--- a/src/include/catalog/dependency.h ++++ b/src/include/catalog/dependency.h +@@ -128,6 +128,7 @@ typedef enum ObjectClass + OCLASS_CONVERSION, /* pg_conversion */ + OCLASS_DEFAULT, /* pg_attrdef */ + OCLASS_LANGUAGE, /* pg_language */ ++ OCLASS_LARGEOBJECT, /* pg_largeobject */ + OCLASS_OPERATOR, /* pg_operator */ + OCLASS_OPCLASS, /* pg_opclass */ + OCLASS_OPFAMILY, /* pg_opfamily */ +diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h +index 81e18a1..0a46611 100644 +--- a/src/include/catalog/indexing.h ++++ b/src/include/catalog/indexing.h +@@ -165,6 +165,9 @@ DECLARE_UNIQUE_INDEX(pg_language_oid_index, 2682, on pg_language using btree(oid + DECLARE_UNIQUE_INDEX(pg_largeobject_loid_pn_index, 2683, on pg_largeobject using btree(loid oid_ops, pageno int4_ops)); + #define LargeObjectLOidPNIndexId 2683 + ++DECLARE_UNIQUE_INDEX(pg_largeobject_metadata_oid_index, 2996, on pg_largeobject_metadata using btree(oid oid_ops)); ++#define LargeObjectMetadataOidIndexId 2996 ++ + DECLARE_UNIQUE_INDEX(pg_namespace_nspname_index, 2684, on pg_namespace using btree(nspname name_ops)); + #define NamespaceNameIndexId 2684 + DECLARE_UNIQUE_INDEX(pg_namespace_oid_index, 2685, on pg_namespace using btree(oid oid_ops)); +diff --git a/src/include/catalog/pg_largeobject.h b/src/include/catalog/pg_largeobject.h +index 5ccfa94..6dd2fb0 100644 +--- a/src/include/catalog/pg_largeobject.h ++++ b/src/include/catalog/pg_largeobject.h +@@ -51,8 +51,9 @@ typedef FormData_pg_largeobject *Form_pg_largeobject; + #define Anum_pg_largeobject_pageno 2 + #define Anum_pg_largeobject_data 3 + +-extern void LargeObjectCreate(Oid loid); ++extern Oid LargeObjectCreate(Oid loid); + extern void LargeObjectDrop(Oid loid); ++extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId); + extern bool LargeObjectExists(Oid loid); + + #endif /* PG_LARGEOBJECT_H */ +diff --git a/src/include/catalog/pg_largeobject_metadata.h b/src/include/catalog/pg_largeobject_metadata.h +new file mode 100644 +index 0000000..e0b6c9a +--- /dev/null ++++ b/src/include/catalog/pg_largeobject_metadata.h +@@ -0,0 +1,52 @@ ++/*------------------------------------------------------------------------- ++ * ++ * pg_largeobject_metadata.h ++ * definition of the system "largeobject_metadata" relation (pg_largeobject_metadata) ++ * along with the relation's initial contents. ++ * ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ * ++ * $PostgreSQL$ ++ * ++ * NOTES ++ * the genbki.sh script reads this file and generates .bki ++ * information from the DATA() statements. ++ * ++ *------------------------------------------------------------------------- ++ */ ++#ifndef PG_LARGEOBJECT_METADATA_H ++#define PG_LARGEOBJECT_METADATA_H ++ ++#include "catalog/genbki.h" ++ ++/* ---------------- ++ * pg_largeobject_metadata definition. cpp turns this into ++ * typedef struct FormData_pg_largeobject_metadata ++ * ---------------- ++ */ ++#define LargeObjectMetadataRelationId 2995 ++ ++CATALOG(pg_largeobject_metadata,2995) ++{ ++ Oid lomowner; /* OID of the largeobject owner */ ++ aclitem lomacl[1]; /* access permissions */ ++} FormData_pg_largeobject_metadata; ++ ++/* ---------------- ++ * Form_pg_largeobject_metadata corresponds to a pointer to a tuple ++ * with the format of pg_largeobject_metadata relation. ++ * ---------------- ++ */ ++typedef FormData_pg_largeobject_metadata *Form_pg_largeobject_metadata; ++ ++/* ---------------- ++ * compiler constants for pg_largeobject_metadata ++ * ---------------- ++ */ ++#define Natts_pg_largeobject_metadata 2 ++#define Anum_pg_largeobject_metadata_lomowner 1 ++#define Anum_pg_largeobject_metadata_lomacl 2 ++ ++#endif /* PG_LARGEOBJECT_METADATA_H */ +diff --git a/src/include/libpq/be-fsstubs.h b/src/include/libpq/be-fsstubs.h +index 5579618..862b014 100644 +--- a/src/include/libpq/be-fsstubs.h ++++ b/src/include/libpq/be-fsstubs.h +@@ -38,6 +38,11 @@ extern Datum lo_unlink(PG_FUNCTION_ARGS); + extern Datum lo_truncate(PG_FUNCTION_ARGS); + + /* ++ * compatibility option for access control ++ */ ++extern bool lo_compat_privileges; ++ ++/* + * These are not fmgr-callable, but are available to C code. + * Probably these should have had the underscore-free names, + * but too late now... +diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h +index 3ff93c4..487a226 100644 +--- a/src/include/nodes/parsenodes.h ++++ b/src/include/nodes/parsenodes.h +@@ -1186,6 +1186,7 @@ typedef enum GrantObjectType + ACL_OBJECT_FOREIGN_SERVER, /* foreign server */ + ACL_OBJECT_FUNCTION, /* function */ + ACL_OBJECT_LANGUAGE, /* procedural language */ ++ ACL_OBJECT_LARGEOBJECT, /* largeobject */ + ACL_OBJECT_NAMESPACE, /* namespace */ + ACL_OBJECT_TABLESPACE /* tablespace */ + } GrantObjectType; +diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h +index bde8727..8799dc0 100644 +--- a/src/include/utils/acl.h ++++ b/src/include/utils/acl.h +@@ -26,6 +26,7 @@ + + #include "nodes/parsenodes.h" + #include "utils/array.h" ++#include "utils/snapshot.h" + + + /* +@@ -151,6 +152,7 @@ typedef ArrayType Acl; + #define ACL_ALL_RIGHTS_FOREIGN_SERVER (ACL_USAGE) + #define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE) + #define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE) ++#define ACL_ALL_RIGHTS_LARGEOBJECT (ACL_SELECT|ACL_UPDATE) + #define ACL_ALL_RIGHTS_NAMESPACE (ACL_USAGE|ACL_CREATE) + #define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE) + +@@ -181,6 +183,7 @@ typedef enum AclObjectKind + ACL_KIND_OPER, /* pg_operator */ + ACL_KIND_TYPE, /* pg_type */ + ACL_KIND_LANGUAGE, /* pg_language */ ++ ACL_KIND_LARGEOBJECT, /* pg_largeobject */ + ACL_KIND_NAMESPACE, /* pg_namespace */ + ACL_KIND_OPCLASS, /* pg_opclass */ + ACL_KIND_OPFAMILY, /* pg_opfamily */ +@@ -273,6 +276,8 @@ extern AclMode pg_proc_aclmask(Oid proc_oid, Oid roleid, + AclMode mask, AclMaskHow how); + extern AclMode pg_language_aclmask(Oid lang_oid, Oid roleid, + AclMode mask, AclMaskHow how); ++extern AclMode pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid, ++ AclMode mask, AclMaskHow how, Snapshot snapshot); + extern AclMode pg_namespace_aclmask(Oid nsp_oid, Oid roleid, + AclMode mask, AclMaskHow how); + extern AclMode pg_tablespace_aclmask(Oid spc_oid, Oid roleid, +@@ -290,6 +295,8 @@ extern AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode); + extern AclResult pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode); + extern AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode); + extern AclResult pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode); ++extern AclResult pg_largeobject_aclcheck_snapshot(Oid lang_oid, Oid roleid, ++ AclMode mode, Snapshot snapshot); + extern AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode); + extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode); + extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode); +@@ -307,6 +314,7 @@ extern bool pg_type_ownercheck(Oid type_oid, Oid roleid); + extern bool pg_oper_ownercheck(Oid oper_oid, Oid roleid); + extern bool pg_proc_ownercheck(Oid proc_oid, Oid roleid); + extern bool pg_language_ownercheck(Oid lan_oid, Oid roleid); ++extern bool pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid); + extern bool pg_namespace_ownercheck(Oid nsp_oid, Oid roleid); + extern bool pg_tablespace_ownercheck(Oid spc_oid, Oid roleid); + extern bool pg_opclass_ownercheck(Oid opc_oid, Oid roleid); +diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out +index a17ff59..a4debf9 100644 +--- a/src/test/regress/expected/privileges.out ++++ b/src/test/regress/expected/privileges.out +@@ -11,6 +11,12 @@ DROP ROLE IF EXISTS regressuser2; + DROP ROLE IF EXISTS regressuser3; + DROP ROLE IF EXISTS regressuser4; + DROP ROLE IF EXISTS regressuser5; ++DROP ROLE IF EXISTS regressuser6; ++SELECT lo_unlink(oid) FROM pg_largeobject_metadata; ++ lo_unlink ++----------- ++(0 rows) ++ + RESET client_min_messages; + -- test proper begins here + CREATE USER regressuser1; +@@ -815,6 +821,194 @@ SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION') + t + (1 row) + ++-- largeobject privilege tests ++\c - ++SET SESSION AUTHORIZATION regressuser1; ++SELECT lo_create(1001); ++ lo_create ++----------- ++ 1001 ++(1 row) ++ ++SELECT lo_create(1002); ++ lo_create ++----------- ++ 1002 ++(1 row) ++ ++SELECT lo_create(1003); ++ lo_create ++----------- ++ 1003 ++(1 row) ++ ++SELECT lo_create(1004); ++ lo_create ++----------- ++ 1004 ++(1 row) ++ ++SELECT lo_create(1005); ++ lo_create ++----------- ++ 1005 ++(1 row) ++ ++GRANT ALL ON LARGE OBJECT 1001 TO PUBLIC; ++GRANT SELECT ON LARGE OBJECT 1003 TO regressuser2; ++GRANT SELECT,UPDATE ON LARGE OBJECT 1004 TO regressuser2; ++GRANT ALL ON LARGE OBJECT 1005 TO regressuser2; ++GRANT SELECT ON LARGE OBJECT 1005 TO regressuser2 WITH GRANT OPTION; ++GRANT SELECT, INSERT ON LARGE OBJECT 1001 TO PUBLIC; -- to be failed ++ERROR: invalid privilege type INSERT for large object ++GRANT SELECT, UPDATE ON LARGE OBJECT 1001 TO nosuchuser; -- to be failed ++ERROR: role "nosuchuser" does not exist ++GRANT SELECT, UPDATE ON LARGE OBJECT 999 TO PUBLIC; -- to be failed ++ERROR: large object 999 does not exist ++\c - ++SET SESSION AUTHORIZATION regressuser2; ++SELECT lo_create(2001); ++ lo_create ++----------- ++ 2001 ++(1 row) ++ ++SELECT lo_create(2002); ++ lo_create ++----------- ++ 2002 ++(1 row) ++ ++SELECT loread(lo_open(1001, x'40000'::int), 32); ++ loread ++-------- ++ ++(1 row) ++ ++SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied ++ERROR: permission denied for large object 1002 ++SELECT loread(lo_open(1003, x'40000'::int), 32); ++ loread ++-------- ++ ++(1 row) ++ ++SELECT loread(lo_open(1004, x'40000'::int), 32); ++ loread ++-------- ++ ++(1 row) ++ ++SELECT lowrite(lo_open(1001, x'20000'::int), 'abcd'); ++ lowrite ++--------- ++ 4 ++(1 row) ++ ++SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied ++ERROR: permission denied for large object 1002 ++SELECT lowrite(lo_open(1003, x'20000'::int), 'abcd'); -- to be denied ++ERROR: permission denied for large object 1003 ++SELECT lowrite(lo_open(1004, x'20000'::int), 'abcd'); ++ lowrite ++--------- ++ 4 ++(1 row) ++ ++GRANT SELECT ON LARGE OBJECT 1005 TO regressuser3; ++GRANT UPDATE ON LARGE OBJECT 1006 TO regressuser3; -- to be denied ++ERROR: large object 1006 does not exist ++REVOKE ALL ON LARGE OBJECT 2001, 2002 FROM PUBLIC; ++GRANT ALL ON LARGE OBJECT 2001 TO regressuser3; ++SELECT lo_unlink(1001); -- to be denied ++ERROR: must be owner of large object 1001 ++SELECT lo_unlink(2002); ++ lo_unlink ++----------- ++ 1 ++(1 row) ++ ++\c - ++-- confirm ACL setting ++SELECT oid, pg_get_userbyid(lomowner) ownername, lomacl FROM pg_largeobject_metadata; ++ oid | ownername | lomacl ++------+--------------+------------------------------------------------------------------------------------------ ++ 1002 | regressuser1 | ++ 1001 | regressuser1 | {regressuser1=rw/regressuser1,=rw/regressuser1} ++ 1003 | regressuser1 | {regressuser1=rw/regressuser1,regressuser2=r/regressuser1} ++ 1004 | regressuser1 | {regressuser1=rw/regressuser1,regressuser2=rw/regressuser1} ++ 1005 | regressuser1 | {regressuser1=rw/regressuser1,regressuser2=r*w/regressuser1,regressuser3=r/regressuser2} ++ 2001 | regressuser2 | {regressuser2=rw/regressuser2,regressuser3=rw/regressuser2} ++(6 rows) ++ ++SET SESSION AUTHORIZATION regressuser3; ++SELECT loread(lo_open(1001, x'40000'::int), 32); ++ loread ++-------- ++ abcd ++(1 row) ++ ++SELECT loread(lo_open(1003, x'40000'::int), 32); -- to be denied ++ERROR: permission denied for large object 1003 ++SELECT loread(lo_open(1005, x'40000'::int), 32); ++ loread ++-------- ++ ++(1 row) ++ ++SELECT lo_truncate(lo_open(1005, x'20000'::int), 10); -- to be denied ++ERROR: permission denied for large object 1005 ++SELECT lo_truncate(lo_open(2001, x'20000'::int), 10); ++ lo_truncate ++------------- ++ 0 ++(1 row) ++ ++-- compatibility mode in largeobject permission ++\c - ++SET lo_compat_privileges = false; -- default setting ++SET SESSION AUTHORIZATION regressuser4; ++SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied ++ERROR: permission denied for large object 1002 ++SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied ++ERROR: permission denied for large object 1002 ++SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -- to be denied ++ERROR: permission denied for large object 1002 ++SELECT lo_unlink(1002); -- to be denied ++ERROR: must be owner of large object 1002 ++SELECT lo_export(1001, '/dev/null'); -- to be denied ++ERROR: must be superuser to use server-side lo_export() ++HINT: Anyone can use the client-side lo_export() provided by libpq. ++\c - ++SET lo_compat_privileges = true; -- compatibility mode ++SET SESSION AUTHORIZATION regressuser4; ++SELECT loread(lo_open(1002, x'40000'::int), 32); ++ loread ++-------- ++ ++(1 row) ++ ++SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); ++ lowrite ++--------- ++ 4 ++(1 row) ++ ++SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); ++ lo_truncate ++------------- ++ 0 ++(1 row) ++ ++SELECT lo_unlink(1002); ++ lo_unlink ++----------- ++ 1 ++(1 row) ++ ++SELECT lo_export(1001, '/dev/null'); -- to be denied ++ERROR: must be superuser to use server-side lo_export() ++HINT: Anyone can use the client-side lo_export() provided by libpq. + -- clean up + \c + DROP FUNCTION testfunc2(int); +@@ -836,6 +1030,16 @@ DROP TABLE atest6; + DROP TABLE atestc; + DROP TABLE atestp1; + DROP TABLE atestp2; ++SELECT lo_unlink(oid) FROM pg_largeobject_metadata; ++ lo_unlink ++----------- ++ 1 ++ 1 ++ 1 ++ 1 ++ 1 ++(5 rows) ++ + DROP GROUP regressgroup1; + DROP GROUP regressgroup2; + REVOKE USAGE ON LANGUAGE sql FROM regressuser1; +@@ -844,3 +1048,5 @@ DROP USER regressuser2; + DROP USER regressuser3; + DROP USER regressuser4; + DROP USER regressuser5; ++DROP USER regressuser6; ++ERROR: role "regressuser6" does not exist +diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out +index c6f1f15..9a66ba0 100644 +--- a/src/test/regress/expected/sanity_check.out ++++ b/src/test/regress/expected/sanity_check.out +@@ -104,6 +104,7 @@ SELECT relname, relhasindex + pg_inherits | t + pg_language | t + pg_largeobject | t ++ pg_largeobject_metadata | t + pg_listener | f + pg_namespace | t + pg_opclass | t +@@ -151,7 +152,7 @@ SELECT relname, relhasindex + timetz_tbl | f + tinterval_tbl | f + varchar_tbl | f +-(140 rows) ++(141 rows) + + -- + -- another sanity check: every system catalog that has OIDs should have +diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql +index 5aa1012..2119aa1 100644 +--- a/src/test/regress/sql/privileges.sql ++++ b/src/test/regress/sql/privileges.sql +@@ -15,6 +15,9 @@ DROP ROLE IF EXISTS regressuser2; + DROP ROLE IF EXISTS regressuser3; + DROP ROLE IF EXISTS regressuser4; + DROP ROLE IF EXISTS regressuser5; ++DROP ROLE IF EXISTS regressuser6; ++ ++SELECT lo_unlink(oid) FROM pg_largeobject_metadata; + + RESET client_min_messages; + +@@ -36,7 +39,6 @@ ALTER GROUP regressgroup2 ADD USER regressuser2; -- duplicate + ALTER GROUP regressgroup2 DROP USER regressuser2; + ALTER GROUP regressgroup2 ADD USER regressuser4; + +- + -- test owner privileges + + SET SESSION AUTHORIZATION regressuser1; +@@ -468,6 +470,83 @@ SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- false + + SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true + ++-- largeobject privilege tests ++\c - ++SET SESSION AUTHORIZATION regressuser1; ++ ++SELECT lo_create(1001); ++SELECT lo_create(1002); ++SELECT lo_create(1003); ++SELECT lo_create(1004); ++SELECT lo_create(1005); ++ ++GRANT ALL ON LARGE OBJECT 1001 TO PUBLIC; ++GRANT SELECT ON LARGE OBJECT 1003 TO regressuser2; ++GRANT SELECT,UPDATE ON LARGE OBJECT 1004 TO regressuser2; ++GRANT ALL ON LARGE OBJECT 1005 TO regressuser2; ++GRANT SELECT ON LARGE OBJECT 1005 TO regressuser2 WITH GRANT OPTION; ++ ++GRANT SELECT, INSERT ON LARGE OBJECT 1001 TO PUBLIC; -- to be failed ++GRANT SELECT, UPDATE ON LARGE OBJECT 1001 TO nosuchuser; -- to be failed ++GRANT SELECT, UPDATE ON LARGE OBJECT 999 TO PUBLIC; -- to be failed ++ ++\c - ++SET SESSION AUTHORIZATION regressuser2; ++ ++SELECT lo_create(2001); ++SELECT lo_create(2002); ++ ++SELECT loread(lo_open(1001, x'40000'::int), 32); ++SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied ++SELECT loread(lo_open(1003, x'40000'::int), 32); ++SELECT loread(lo_open(1004, x'40000'::int), 32); ++ ++SELECT lowrite(lo_open(1001, x'20000'::int), 'abcd'); ++SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied ++SELECT lowrite(lo_open(1003, x'20000'::int), 'abcd'); -- to be denied ++SELECT lowrite(lo_open(1004, x'20000'::int), 'abcd'); ++ ++GRANT SELECT ON LARGE OBJECT 1005 TO regressuser3; ++GRANT UPDATE ON LARGE OBJECT 1006 TO regressuser3; -- to be denied ++REVOKE ALL ON LARGE OBJECT 2001, 2002 FROM PUBLIC; ++GRANT ALL ON LARGE OBJECT 2001 TO regressuser3; ++ ++SELECT lo_unlink(1001); -- to be denied ++SELECT lo_unlink(2002); ++ ++\c - ++-- confirm ACL setting ++SELECT oid, pg_get_userbyid(lomowner) ownername, lomacl FROM pg_largeobject_metadata; ++ ++SET SESSION AUTHORIZATION regressuser3; ++ ++SELECT loread(lo_open(1001, x'40000'::int), 32); ++SELECT loread(lo_open(1003, x'40000'::int), 32); -- to be denied ++SELECT loread(lo_open(1005, x'40000'::int), 32); ++ ++SELECT lo_truncate(lo_open(1005, x'20000'::int), 10); -- to be denied ++SELECT lo_truncate(lo_open(2001, x'20000'::int), 10); ++ ++-- compatibility mode in largeobject permission ++\c - ++SET lo_compat_privileges = false; -- default setting ++SET SESSION AUTHORIZATION regressuser4; ++ ++SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied ++SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied ++SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -- to be denied ++SELECT lo_unlink(1002); -- to be denied ++SELECT lo_export(1001, '/dev/null'); -- to be denied ++ ++\c - ++SET lo_compat_privileges = true; -- compatibility mode ++SET SESSION AUTHORIZATION regressuser4; ++ ++SELECT loread(lo_open(1002, x'40000'::int), 32); ++SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); ++SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); ++SELECT lo_unlink(1002); ++SELECT lo_export(1001, '/dev/null'); -- to be denied + + -- clean up + +@@ -493,6 +572,8 @@ DROP TABLE atestc; + DROP TABLE atestp1; + DROP TABLE atestp2; + ++SELECT lo_unlink(oid) FROM pg_largeobject_metadata; ++ + DROP GROUP regressgroup1; + DROP GROUP regressgroup2; + +@@ -502,3 +583,4 @@ DROP USER regressuser2; + DROP USER regressuser3; + DROP USER regressuser4; + DROP USER regressuser5; ++DROP USER regressuser6; diff --git a/pgsql-02-8.4-sepgsql.patch b/pgsql-02-8.4-sepgsql.patch new file mode 100644 index 0000000..9bd8c12 --- /dev/null +++ b/pgsql-02-8.4-sepgsql.patch @@ -0,0 +1,17591 @@ +diff --git a/configure b/configure +index 98a55b5..ddbdb07 100755 +--- a/configure ++++ b/configure +@@ -710,6 +710,8 @@ with_libxml + with_libxslt + with_system_tzdata + with_zlib ++enable_selinux ++SELINUX_LIBS + GREP + EGREP + ELF_SYS +@@ -1378,6 +1380,7 @@ Optional Features: + --enable-thread-safety make client libraries thread-safe + --enable-thread-safety-force + force thread-safety despite thread test failure ++ --enable-selinux enable to build with SELinux support + --disable-float4-byval disable float4 passed by value + --disable-float8-byval disable float8 passed by value + --disable-largefile omit support for large files +@@ -5532,6 +5535,183 @@ fi + + + # ++# SELinux support ++# ++ ++pgac_args="$pgac_args enable_selinux" ++ ++# Check whether --enable-selinux was given. ++if test "${enable_selinux+set}" = set; then ++ enableval=$enable_selinux; ++ case $enableval in ++ yes) ++ : ++ ;; ++ no) ++ : ++ ;; ++ *) ++ { { echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5 ++echo "$as_me: error: no argument expected for --enable-selinux option" >&2;} ++ { (exit 1); exit 1; }; } ++ ;; ++ esac ++ ++else ++ enable_selinux=no ++ ++fi ++ ++ ++if test "$enable_selinux" = yes; then ++ SELINUX_LIBS="-lselinux" ++ { echo "$as_me:$LINENO: checking for avc_netlink_loop in -lselinux" >&5 ++echo $ECHO_N "checking for avc_netlink_loop in -lselinux... $ECHO_C" >&6; } ++if test "${ac_cv_lib_selinux_avc_netlink_loop+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lselinux $LIBS" ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char avc_netlink_loop (); ++int ++main () ++{ ++return avc_netlink_loop (); ++ ; ++ return 0; ++} ++_ACEOF ++rm -f conftest.$ac_objext conftest$ac_exeext ++if { (ac_try="$ac_link" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_link") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest$ac_exeext && ++ $as_test_x conftest$ac_exeext; then ++ ac_cv_lib_selinux_avc_netlink_loop=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_cv_lib_selinux_avc_netlink_loop=no ++fi ++ ++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_avc_netlink_loop" >&5 ++echo "${ECHO_T}$ac_cv_lib_selinux_avc_netlink_loop" >&6; } ++if test $ac_cv_lib_selinux_avc_netlink_loop = yes; then ++ ++cat >>confdefs.h <<_ACEOF ++#define HAVE_SELINUX 1 ++_ACEOF ++ ++else ++ { { echo "$as_me:$LINENO: error: \"--enable-selinux requires libselinux.\"" >&5 ++echo "$as_me: error: \"--enable-selinux requires libselinux.\"" >&2;} ++ { (exit 1); exit 1; }; } ++fi ++ ++ { echo "$as_me:$LINENO: checking for audit_open in -laudit" >&5 ++echo $ECHO_N "checking for audit_open in -laudit... $ECHO_C" >&6; } ++if test "${ac_cv_lib_audit_audit_open+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-laudit $LIBS" ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char audit_open (); ++int ++main () ++{ ++return audit_open (); ++ ; ++ return 0; ++} ++_ACEOF ++rm -f conftest.$ac_objext conftest$ac_exeext ++if { (ac_try="$ac_link" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++ (eval "$ac_link") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest$ac_exeext && ++ $as_test_x conftest$ac_exeext; then ++ ac_cv_lib_audit_audit_open=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_cv_lib_audit_audit_open=no ++fi ++ ++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ echo "$as_me:$LINENO: result: $ac_cv_lib_audit_audit_open" >&5 ++echo "${ECHO_T}$ac_cv_lib_audit_audit_open" >&6; } ++if test $ac_cv_lib_audit_audit_open = yes; then ++ cat >>confdefs.h <<_ACEOF ++#define HAVE_LIBAUDIT 1 ++_ACEOF ++ ++ SELINUX_LIBS="$SELINUX_LIBS -laudit" ++fi ++ ++ ++ ++fi ++ ++# + # Elf + # + +@@ -28137,11 +28317,11 @@ with_libxml!$with_libxml$ac_delim + with_libxslt!$with_libxslt$ac_delim + with_system_tzdata!$with_system_tzdata$ac_delim + with_zlib!$with_zlib$ac_delim ++enable_selinux!$enable_selinux$ac_delim ++SELINUX_LIBS!$SELINUX_LIBS$ac_delim + GREP!$GREP$ac_delim + EGREP!$EGREP$ac_delim + ELF_SYS!$ELF_SYS$ac_delim +-LDFLAGS_SL!$LDFLAGS_SL$ac_delim +-LD!$LD$ac_delim + _ACEOF + + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then +@@ -28183,6 +28363,8 @@ _ACEOF + ac_delim='%!_!# ' + for ac_last_try in false false false false false :; do + cat >conf$$subs.sed <<_ACEOF ++LDFLAGS_SL!$LDFLAGS_SL$ac_delim ++LD!$LD$ac_delim + with_gnu_ld!$with_gnu_ld$ac_delim + ld_R_works!$ld_R_works$ac_delim + RANLIB!$RANLIB$ac_delim +@@ -28245,7 +28427,7 @@ vpath_build!$vpath_build$ac_delim + LTLIBOBJS!$LTLIBOBJS$ac_delim + _ACEOF + +- if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 60; then ++ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 62; then + break + elif $ac_last_try; then + { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +diff --git a/configure.in b/configure.in +index 1a68732..16f6eab 100644 +--- a/configure.in ++++ b/configure.in +@@ -764,6 +764,24 @@ PGAC_ARG_BOOL(with, zlib, yes, + AC_SUBST(with_zlib) + + # ++# SELinux support ++# ++PGAC_ARG_BOOL(enable, selinux, no, ++ [enable to build with SELinux support]) ++if test "$enable_selinux" = yes; then ++ SELINUX_LIBS="-lselinux" ++ AC_CHECK_LIB(selinux, avc_netlink_loop, ++ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, ++ [SE-PostgreSQL feature is enabled]), ++ AC_MSG_ERROR("--enable-selinux requires libselinux.")) ++ AC_CHECK_LIB(audit, audit_open, ++ AC_DEFINE_UNQUOTED(HAVE_LIBAUDIT, 1) ++ SELINUX_LIBS="$SELINUX_LIBS -laudit") ++ AC_SUBST(enable_selinux) ++ AC_SUBST(SELINUX_LIBS) ++fi ++ ++# + # Elf + # + +diff --git a/src/Makefile.global.in b/src/Makefile.global.in +index 6244e8a..39e2493 100644 +--- a/src/Makefile.global.in ++++ b/src/Makefile.global.in +@@ -165,6 +165,7 @@ enable_nls = @enable_nls@ + enable_debug = @enable_debug@ + enable_dtrace = @enable_dtrace@ + enable_coverage = @enable_coverage@ ++enable_selinux = @enable_selinux@ + enable_thread_safety = @enable_thread_safety@ + + python_includespec = @python_includespec@ +@@ -184,6 +185,8 @@ TCL_INCLUDE_SPEC = @TCL_INCLUDE_SPEC@ + TCL_SHARED_BUILD = @TCL_SHARED_BUILD@ + TCL_SHLIB_LD_LIBS = @TCL_SHLIB_LD_LIBS@ + ++SELINUX_LIBS = @SELINUX_LIBS@ ++ + PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ + PTHREAD_LIBS = @PTHREAD_LIBS@ + +diff --git a/src/backend/Makefile b/src/backend/Makefile +index baa45e1..bc13bb9 100644 +--- a/src/backend/Makefile ++++ b/src/backend/Makefile +@@ -16,7 +16,7 @@ include $(top_builddir)/src/Makefile.global + + SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ + main nodes optimizer port postmaster regex rewrite \ +- storage tcop tsearch utils $(top_builddir)/src/timezone ++ security storage tcop tsearch utils $(top_builddir)/src/timezone + + include $(srcdir)/common.mk + +@@ -40,6 +40,9 @@ LIBS := $(filter-out -lpgport, $(LIBS)) $(LDAP_LIBS_BE) + # The backend doesn't need everything that's in LIBS, however + LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) + ++# SELinux Libraries ++LIBS += $(SELINUX_LIBS) ++ + ########################################################################## + + all: submake-libpgport postgres $(POSTGRES_IMP) +diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c +index a86716e..7c4d1f6 100644 +--- a/src/backend/access/common/heaptuple.c ++++ b/src/backend/access/common/heaptuple.c +@@ -60,6 +60,7 @@ + #include "access/heapam.h" + #include "access/sysattr.h" + #include "access/tuptoaster.h" ++#include "catalog/pg_security.h" + #include "executor/tuptable.h" + + +@@ -287,6 +288,7 @@ heap_attisnull(HeapTuple tup, int attnum) + case MinCommandIdAttributeNumber: + case MaxTransactionIdAttributeNumber: + case MaxCommandIdAttributeNumber: ++ case SecurityAttributeNumber: + /* these are never null */ + break; + +@@ -599,6 +601,9 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) + case TableOidAttributeNumber: + result = ObjectIdGetDatum(tup->t_tableOid); + break; ++ case SecurityAttributeNumber: ++ result = securitySysattSecLabelOut(tup->t_tableOid, tup); ++ break; + default: + elog(ERROR, "invalid attnum: %d", attnum); + result = 0; /* keep compiler quiet */ +@@ -722,6 +727,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, + + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); ++ if (tupleDescriptor->tdhassecid) ++ len += sizeof(Oid); + + hoff = len = MAXALIGN(len); /* align user data safely */ + +@@ -753,6 +760,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, + + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + td->t_infomask = HEAP_HASOID; ++ if (tupleDescriptor->tdhassecid) ++ td->t_infomask |= HEAP_HASSECID; + + heap_fill_tuple(tupleDescriptor, + values, +@@ -864,6 +873,8 @@ heap_modify_tuple(HeapTuple tuple, + newTuple->t_tableOid = tuple->t_tableOid; + if (tupleDesc->tdhasoid) + HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); ++ if (HeapTupleHasSecid(newTuple)) ++ HeapTupleSetSecid(newTuple, HeapTupleGetSecid(tuple)); + + return newTuple; + } +@@ -1474,6 +1485,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor, + + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); ++ if (tupleDescriptor->tdhassecid) ++ len += sizeof(Oid); + + hoff = len = MAXALIGN(len); /* align user data safely */ + +@@ -1495,6 +1508,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor, + + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + tuple->t_infomask = HEAP_HASOID; ++ if (tupleDescriptor->tdhassecid) ++ tuple->t_infomask |= HEAP_HASSECID; + + heap_fill_tuple(tupleDescriptor, + values, +diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c +index 8582a7d..7df1631 100644 +--- a/src/backend/access/common/tupdesc.c ++++ b/src/backend/access/common/tupdesc.c +@@ -88,6 +88,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid) + desc->tdtypeid = RECORDOID; + desc->tdtypmod = -1; + desc->tdhasoid = hasoid; ++ desc->tdhassecid = false; + desc->tdrefcount = -1; /* assume not reference-counted */ + + return desc; +@@ -121,6 +122,7 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs) + desc->tdtypeid = RECORDOID; + desc->tdtypmod = -1; + desc->tdhasoid = hasoid; ++ desc->tdhassecid = false; + desc->tdrefcount = -1; /* assume not reference-counted */ + + return desc; +@@ -150,6 +152,7 @@ CreateTupleDescCopy(TupleDesc tupdesc) + + desc->tdtypeid = tupdesc->tdtypeid; + desc->tdtypmod = tupdesc->tdtypmod; ++ desc->tdhassecid = tupdesc->tdhassecid; + + return desc; + } +@@ -208,6 +211,7 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) + + desc->tdtypeid = tupdesc->tdtypeid; + desc->tdtypmod = tupdesc->tdtypmod; ++ desc->tdhassecid = tupdesc->tdhassecid; + + return desc; + } +@@ -314,6 +318,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) + return false; + if (tupdesc1->tdhasoid != tupdesc2->tdhasoid) + return false; ++ if (tupdesc1->tdhassecid != tupdesc2->tdhassecid) ++ return false; + + for (i = 0; i < tupdesc1->natts; i++) + { +diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c +index b0a911e..697946b 100644 +--- a/src/backend/access/heap/heapam.c ++++ b/src/backend/access/heap/heapam.c +@@ -54,6 +54,7 @@ + #include "catalog/namespace.h" + #include "miscadmin.h" + #include "pgstat.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/freespace.h" + #include "storage/lmgr.h" +@@ -2016,6 +2017,12 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid, + Oid + simple_heap_insert(Relation relation, HeapTuple tup) + { ++ /* ++ * SELinux assigns default security label for the tuple, ++ * but does not check permissions to the internal operations. ++ */ ++ sepgsqlHeapTupleInsert(relation, tup, true); ++ + return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL); + } + +@@ -2558,6 +2565,11 @@ l2: + Assert(!(newtup->t_data->t_infomask & HEAP_HASOID)); + } + ++ /* Preserve SecurityId, if not changed */ ++ if (HeapTupleHasSecid(newtup) && ++ !OidIsValid(HeapTupleGetSecid(newtup))) ++ HeapTupleSetSecid(newtup, HeapTupleGetSecid(&oldtup)); ++ + newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK); + newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); + newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED); +@@ -3499,6 +3511,8 @@ heap_inplace_update(Relation relation, HeapTuple tuple) + memcpy((char *) htup + htup->t_hoff, + (char *) tuple->t_data + tuple->t_data->t_hoff, + newlen); ++ if (HeapTupleHeaderGetSecid(htup) != HeapTupleGetSecid(tuple)) ++ HeapTupleHeaderSetSecid(htup, HeapTupleGetSecid(tuple)); + + MarkBufferDirty(buffer); + +diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c +index 7b2ebe5..6179e52 100644 +--- a/src/backend/access/heap/tuptoaster.c ++++ b/src/backend/access/heap/tuptoaster.c +@@ -591,6 +591,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, + hoff += BITMAPLEN(numAttrs); + if (newtup->t_data->t_infomask & HEAP_HASOID) + hoff += sizeof(Oid); ++ if (HeapTupleHasSecid(newtup)) ++ hoff += sizeof(Oid); + hoff = MAXALIGN(hoff); + Assert(hoff == newtup->t_data->t_hoff); + /* now convert to a limit on the tuple data size */ +@@ -864,6 +866,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, + new_len += BITMAPLEN(numAttrs); + if (olddata->t_infomask & HEAP_HASOID) + new_len += sizeof(Oid); ++ if (HeapTupleHeaderHasSecid(olddata)) ++ new_len += sizeof(Oid); + new_len = MAXALIGN(new_len); + Assert(new_len == olddata->t_hoff); + new_data_len = heap_compute_data_size(tupleDesc, +@@ -1015,6 +1019,8 @@ toast_flatten_tuple_attribute(Datum value, + new_len += BITMAPLEN(numAttrs); + if (olddata->t_infomask & HEAP_HASOID) + new_len += sizeof(Oid); ++ if (HeapTupleHeaderHasSecid(olddata)) ++ new_len += sizeof(Oid); + new_len = MAXALIGN(new_len); + Assert(new_len == olddata->t_hoff); + new_data_len = heap_compute_data_size(tupleDesc, +@@ -1213,6 +1219,12 @@ toast_save_datum(Relation rel, Datum value, int options) + memcpy(VARDATA(&chunk_data), data_p, chunk_size); + toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull); + ++ /* ++ * NOTE: SE-PostgreSQL does not assign any security label ++ * for tuples within the TOASTVALUE relation, so we omit ++ * to put sepgsqlHeapTupleInsert() hook here. ++ */ ++ + heap_insert(toastrel, toasttup, mycid, options, NULL); + + /* +diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c +index abf1ca1..e6db40a 100644 +--- a/src/backend/access/transam/xact.c ++++ b/src/backend/access/transam/xact.c +@@ -36,6 +36,8 @@ + #include "libpq/be-fsstubs.h" + #include "miscadmin.h" + #include "pgstat.h" ++#include "security/rowlevel.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "storage/lmgr.h" +@@ -140,6 +142,8 @@ typedef struct TransactionStateData + Oid prevUser; /* previous CurrentUserId setting */ + int prevSecContext; /* previous SecurityRestrictionContext */ + bool prevXactReadOnly; /* entry-time xact r/o state */ ++ char *prevSecLabel; /* previous security label of client */ ++ int prevRowlv; /* previous Row-level control behavior */ + struct TransactionStateData *parent; /* back link to parent */ + } TransactionStateData; + +@@ -168,6 +172,8 @@ static TransactionStateData TopTransactionStateData = { + InvalidOid, /* previous CurrentUserId setting */ + 0, /* previous SecurityRestrictionContext */ + false, /* entry-time xact r/o state */ ++ NULL, /* previous security label of client */ ++ ROWLV_FILTER_MODE, /* previous Row-level control behavior */ + NULL /* link to parent state block */ + }; + +@@ -1527,6 +1533,9 @@ StartTransaction(void) + /* SecurityRestrictionContext should never be set outside a transaction */ + Assert(s->prevSecContext == 0); + ++ s->prevSecLabel = sepgsqlGetClientLabel(); ++ s->prevRowlv = rowlvGetPerformingMode(); ++ + /* + * initialize other subsystems for new transaction + */ +@@ -2031,6 +2040,12 @@ AbortTransaction(void) + SetUserIdAndSecContext(s->prevUser, s->prevSecContext); + + /* ++ * Reset SELinux features ++ */ ++ sepgsqlSetClientLabel(s->prevSecLabel); ++ rowlvSetPerformingMode(s->prevRowlv); ++ ++ /* + * do abort processing + */ + AfterTriggerEndXact(false); +@@ -3874,6 +3889,12 @@ AbortSubTransaction(void) + SetUserIdAndSecContext(s->prevUser, s->prevSecContext); + + /* ++ * Reset SELinux features ++ */ ++ sepgsqlSetClientLabel(s->prevSecLabel); ++ rowlvSetPerformingMode(s->prevRowlv); ++ ++ /* + * We can skip all this stuff if the subxact failed before creating a + * ResourceOwner... + */ +@@ -4015,6 +4036,8 @@ PushTransaction(void) + s->blockState = TBLOCK_SUBBEGIN; + GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); + s->prevXactReadOnly = XactReadOnly; ++ s->prevSecLabel = sepgsqlGetClientLabel(); ++ s->prevRowlv = rowlvGetPerformingMode(); + + CurrentTransactionState = s; + +diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y +index 8d3f6c2..8700483 100644 +--- a/src/backend/bootstrap/bootparse.y ++++ b/src/backend/bootstrap/bootparse.y +@@ -42,6 +42,7 @@ + #include "nodes/pg_list.h" + #include "nodes/primnodes.h" + #include "rewrite/prs2lock.h" ++#include "security/sepgsql.h" + #include "storage/block.h" + #include "storage/fd.h" + #include "storage/ipc.h" +@@ -211,6 +212,13 @@ Boot_CreateStmt: + else + { + Oid id; ++ Oid *secLabels = ++ sepgsql_relation_create(LexIDStr($5), ++ RELKIND_RELATION, ++ tupdesc, ++ PG_CATALOG_NAMESPACE, ++ NULL, NIL, ++ false, false); + + id = heap_create_with_catalog(LexIDStr($5), + PG_CATALOG_NAMESPACE, +@@ -225,7 +233,8 @@ Boot_CreateStmt: + 0, + ONCOMMIT_NOOP, + (Datum) 0, +- true); ++ true, ++ secLabels); + elog(DEBUG4, "relation created with oid %u", id); + } + do_end(); +diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c +index 13d5bcb..0de0c85 100644 +--- a/src/backend/bootstrap/bootstrap.c ++++ b/src/backend/bootstrap/bootstrap.c +@@ -26,12 +26,14 @@ + #include "access/xact.h" + #include "bootstrap/bootstrap.h" + #include "catalog/index.h" ++#include "catalog/pg_security.h" + #include "catalog/pg_type.h" + #include "libpq/pqsignal.h" + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "postmaster/bgwriter.h" + #include "postmaster/walwriter.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/ipc.h" + #include "storage/proc.h" +@@ -338,6 +340,11 @@ AuxiliaryProcessMain(int argc, char *argv[]) + case WalWriterProcess: + statmsg = "wal writer process"; + break; ++#ifdef HAVE_SELINUX ++ case SelinuxReceiverProcess: ++ statmsg = "selinux netlink receiver"; ++ break; ++#endif + default: + statmsg = "??? process"; + break; +@@ -430,6 +437,12 @@ AuxiliaryProcessMain(int argc, char *argv[]) + WalWriterMain(); + proc_exit(1); /* should never return */ + ++#ifdef HAVE_SELINUX ++ case SelinuxReceiverProcess: ++ sepgsqlReceiverMain(); ++ proc_exit(1); /* should nener return */ ++#endif ++ + default: + elog(PANIC, "unrecognized process type: %d", auxType); + proc_exit(1); +@@ -497,6 +510,11 @@ BootstrapModeMain(void) + */ + boot_yyparse(); + ++ /* ++ * SELinux initial labeling ++ */ ++ sepgsqlPostBootstrapingMode(); ++ + /* Perform a checkpoint to ensure everything's down to disk */ + SetProcessingMode(NormalProcessing); + CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); +@@ -794,6 +812,8 @@ InsertOneTuple(Oid objectid) + tupDesc = CreateTupleDesc(numattr, + RelationGetForm(boot_reldesc)->relhasoids, + attrtypes); ++ tupDesc->tdhassecid = RelationGetDescr(boot_reldesc)->tdhassecid; ++ + tuple = heap_form_tuple(tupDesc, values, Nulls); + if (objectid != (Oid) 0) + HeapTupleSetOid(tuple, objectid); +diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile +index 3644ca6..632e3ed 100644 +--- a/src/backend/catalog/Makefile ++++ b/src/backend/catalog/Makefile +@@ -13,7 +13,7 @@ include $(top_builddir)/src/Makefile.global + OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ + pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ + pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ +- pg_shdepend.o pg_type.o storage.o toasting.o ++ pg_security.o pg_shdepend.o pg_type.o storage.o toasting.o + + BKIFILES = postgres.bki postgres.description postgres.shdescription + +@@ -34,7 +34,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ + pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ + pg_database.h pg_tablespace.h pg_pltemplate.h \ + pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ +- pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ ++ pg_security.h pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ + pg_ts_parser.h pg_ts_template.h \ + pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ + toasting.h indexing.h \ +diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c +index 1be417c..6139e4b 100644 +--- a/src/backend/catalog/aclchk.c ++++ b/src/backend/catalog/aclchk.c +@@ -37,6 +37,7 @@ + #include "catalog/pg_operator.h" + #include "catalog/pg_opfamily.h" + #include "catalog/pg_proc.h" ++#include "catalog/pg_security.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_type.h" + #include "catalog/pg_ts_config.h" +@@ -45,6 +46,7 @@ + #include "foreign/foreign.h" + #include "miscadmin.h" + #include "parser/parse_func.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" +@@ -699,6 +701,12 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, + if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) + continue; + ++ /* Skip OID column, if it doesn't exist */ ++ if (curr_att == SecurityAttributeNumber && ++ (classForm->relkind != RELKIND_RELATION || ++ table_oid == SecurityRelationId)) ++ continue; ++ + /* Views don't have any system columns at all */ + if (classForm->relkind == RELKIND_VIEW && curr_att < 0) + continue; +@@ -801,6 +809,8 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, + relOid, grantorId, ACL_KIND_COLUMN, + relname, attnum, + NameStr(pg_attribute_tuple->attname)); ++ /* SELinux checks */ ++ sepgsql_attribute_grant(relOid, attnum); + + /* + * Generate new ACL. +@@ -1056,6 +1066,8 @@ ExecGrant_Relation(InternalGrant *istmt) + ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, + NameStr(pg_class_tuple->relname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_relation_grant(relOid); + + /* + * Generate new ACL. +@@ -1244,6 +1256,8 @@ ExecGrant_Database(InternalGrant *istmt) + datId, grantorId, ACL_KIND_DATABASE, + NameStr(pg_database_tuple->datname), + 0, NULL); ++ /* SELinux permission checks */ ++ sepgsql_database_grant(datId); + + /* + * Generate new ACL. +@@ -1362,6 +1376,8 @@ ExecGrant_Fdw(InternalGrant *istmt) + fdwid, grantorId, ACL_KIND_FDW, + NameStr(pg_fdw_tuple->fdwname), + 0, NULL); ++ /* SELinux permission checks */ ++ sepgsql_fdw_grant(fdwid); + + /* + * Generate new ACL. +@@ -1481,6 +1497,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) + srvid, grantorId, ACL_KIND_FOREIGN_SERVER, + NameStr(pg_server_tuple->srvname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_foreign_server_grant(srvid); + + /* + * Generate new ACL. +@@ -1599,6 +1617,8 @@ ExecGrant_Function(InternalGrant *istmt) + funcId, grantorId, ACL_KIND_PROC, + NameStr(pg_proc_tuple->proname), + 0, NULL); ++ /* SELinux: db_procedure:{setattr} */ ++ sepgsql_proc_grant(funcId); + + /* + * Generate new ACL. +@@ -1723,6 +1743,8 @@ ExecGrant_Language(InternalGrant *istmt) + langId, grantorId, ACL_KIND_LANGUAGE, + NameStr(pg_language_tuple->lanname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_language_grant(langId); + + /* + * Generate new ACL. +@@ -1974,6 +1996,9 @@ ExecGrant_Namespace(InternalGrant *istmt) + NameStr(pg_namespace_tuple->nspname), + 0, NULL); + ++ /* SELinux: db_schema:{setattr} */ ++ sepgsql_schema_grant(nspid); ++ + /* + * Generate new ACL. + * +diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c +index d168694..efb4333 100644 +--- a/src/backend/catalog/catalog.c ++++ b/src/backend/catalog/catalog.c +@@ -31,6 +31,7 @@ + #include "catalog/pg_database.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_pltemplate.h" ++#include "catalog/pg_security.h" + #include "catalog/pg_shdepend.h" + #include "catalog/pg_shdescription.h" + #include "catalog/pg_tablespace.h" +@@ -304,6 +305,7 @@ IsSharedRelation(Oid relationId) + relationId == AuthMemRelationId || + relationId == DatabaseRelationId || + relationId == PLTemplateRelationId || ++ relationId == SecurityRelationId || + relationId == SharedDescriptionRelationId || + relationId == SharedDependRelationId || + relationId == TableSpaceRelationId) +@@ -316,6 +318,8 @@ IsSharedRelation(Oid relationId) + relationId == DatabaseNameIndexId || + relationId == DatabaseOidIndexId || + relationId == PLTemplateNameIndexId || ++ relationId == SecuritySecidIndexId || ++ relationId == SecuritySecattrIndexId || + relationId == SharedDescriptionObjIndexId || + relationId == SharedDependDependerIndexId || + relationId == SharedDependReferenceIndexId || +@@ -327,6 +331,8 @@ IsSharedRelation(Oid relationId) + relationId == PgAuthidToastIndex || + relationId == PgDatabaseToastTable || + relationId == PgDatabaseToastIndex || ++ relationId == PgSecurityToastTable || ++ relationId == PgSecurityToastIndex || + relationId == PgShdescriptionToastTable || + relationId == PgShdescriptionToastIndex) + return true; +diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c +index 416c149..4e447f0 100644 +--- a/src/backend/catalog/dependency.c ++++ b/src/backend/catalog/dependency.c +@@ -64,6 +64,7 @@ + #include "nodes/nodeFuncs.h" + #include "parser/parsetree.h" + #include "rewrite/rewriteRemove.h" ++#include "security/sepgsql.h" + #include "storage/lmgr.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -162,7 +163,8 @@ static void reportDependentObjects(const ObjectAddresses *targetObjects, + DropBehavior behavior, + int msglevel, + const ObjectAddress *origObject); +-static void deleteOneObject(const ObjectAddress *object, Relation depRel); ++static void deleteOneObject(const ObjectAddress *object, ++ Relation depRel, bool permission); + static void doDeletion(const ObjectAddress *object); + static void AcquireDeletionLock(const ObjectAddress *object); + static void ReleaseDeletionLock(const ObjectAddress *object); +@@ -194,9 +196,9 @@ static void getOpFamilyDescription(StringInfo buffer, Oid opfid); + * are variants on the same theme; if you change anything here you'll likely + * need to fix them too. + */ +-void +-performDeletion(const ObjectAddress *object, +- DropBehavior behavior) ++static void ++performDeletionInternal(const ObjectAddress *object, ++ DropBehavior behavior, bool permission) + { + Relation depRel; + ObjectAddresses *targetObjects; +@@ -242,7 +244,7 @@ performDeletion(const ObjectAddress *object, + { + ObjectAddress *thisobj = targetObjects->refs + i; + +- deleteOneObject(thisobj, depRel); ++ deleteOneObject(thisobj, depRel, permission); + } + + /* And clean up */ +@@ -251,6 +253,18 @@ performDeletion(const ObjectAddress *object, + heap_close(depRel, RowExclusiveLock); + } + ++void ++performDeletion(const ObjectAddress *object, DropBehavior behavior) ++{ ++ performDeletionInternal(object, behavior, true); ++} ++ ++void ++performDeletionNoPerms(const ObjectAddress *object, DropBehavior behavior) ++{ ++ performDeletionInternal(object, behavior, false); ++} ++ + /* + * performMultipleDeletions: Similar to performDeletion, but act on multiple + * objects at once. +@@ -324,7 +338,8 @@ performMultipleDeletions(const ObjectAddresses *objects, + { + ObjectAddress *thisobj = targetObjects->refs + i; + +- deleteOneObject(thisobj, depRel); ++ /* currently, all the caller path need permission checks */ ++ deleteOneObject(thisobj, depRel, true); + } + + /* And clean up */ +@@ -395,7 +410,7 @@ deleteWhatDependsOn(const ObjectAddress *object, + if (thisextra->flags & DEPFLAG_ORIGINAL) + continue; + +- deleteOneObject(thisobj, depRel); ++ deleteOneObject(thisobj, depRel, false); + } + + /* And clean up */ +@@ -945,13 +960,17 @@ reportDependentObjects(const ObjectAddresses *targetObjects, + * depRel is the already-open pg_depend relation. + */ + static void +-deleteOneObject(const ObjectAddress *object, Relation depRel) ++deleteOneObject(const ObjectAddress *object, Relation depRel, bool permission) + { + ScanKeyData key[3]; + int nkeys; + SysScanDesc scan; + HeapTuple tup; + ++ /* SELinux checks db_xxx:{drop}, if necessary */ ++ if (permission) ++ sepgsql_sysobj_drop(object); ++ + /* + * First remove any pg_depend records that link from this object to + * others. (Any records linking to this object should be gone already.) +diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c +index 7557400..b5161c7 100644 +--- a/src/backend/catalog/heap.c ++++ b/src/backend/catalog/heap.c +@@ -43,6 +43,7 @@ + #include "catalog/pg_constraint.h" + #include "catalog/pg_inherits.h" + #include "catalog/pg_namespace.h" ++#include "catalog/pg_security.h" + #include "catalog/pg_statistic.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_type.h" +@@ -56,6 +57,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_expr.h" + #include "parser/parse_relation.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/freespace.h" + #include "storage/smgr.h" +@@ -74,7 +76,8 @@ static void AddNewRelationTuple(Relation pg_class_desc, + Oid new_rel_oid, Oid new_type_oid, + Oid relowner, + char relkind, +- Datum reloptions); ++ Datum reloptions, ++ Oid *secLabels); + static Oid AddNewRelationType(const char *typeName, + Oid typeNamespace, + Oid new_rel_oid, +@@ -158,7 +161,16 @@ static FormData_pg_attribute a7 = { + true, 'p', 'i', true, false, false, true, 0, {0} + }; + +-static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; ++/* ++ * System columns for enhanced security features ++ */ ++static FormData_pg_attribute a8 = { ++ 0, {SecurityAttributeName}, TEXTOID, 0, -1, ++ SecurityAttributeNumber, 0, -1, -1, ++ false, 'x', 'i', true, false, false, true, 0, {0} ++}; ++ ++static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8}; + + /* + * This function returns a Form_pg_attribute pointer for a system attribute. +@@ -198,6 +210,17 @@ SystemAttributeByName(const char *attname, bool relhasoids) + return NULL; + } + ++/* ++ * If the given attribute number is writable, returns true. ++ */ ++bool ++SystemAttributeIsWritable(AttrNumber attnum) ++{ ++ if (attnum == SecurityAttributeNumber) ++ return true; ++ ++ return false; ++} + + /* ---------------------------------------------------------------- + * XXX END OF UGLY HARD CODED BADNESS XXX +@@ -293,6 +316,11 @@ heap_create(const char *relname, + relid, + reltablespace, + shared_relation); ++ /* ++ * Does the relation have security attribute? ++ */ ++ RelationGetDescr(rel)->tdhassecid ++ = securityTupleDescHasSecid(relid, relkind); + + /* + * Have the storage manager create the relation's disk file, if needed. +@@ -487,7 +515,8 @@ CheckAttributeType(const char *attname, Oid atttypid) + void + InsertPgAttributeTuple(Relation pg_attribute_rel, + Form_pg_attribute new_attribute, +- CatalogIndexState indstate) ++ CatalogIndexState indstate, ++ Oid new_att_secid) + { + Datum values[Natts_pg_attribute]; + bool nulls[Natts_pg_attribute]; +@@ -520,6 +549,9 @@ InsertPgAttributeTuple(Relation pg_attribute_rel, + + tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls); + ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, new_att_secid); ++ + /* finally insert the new tuple, update the indexes, and clean up */ + simple_heap_insert(pg_attribute_rel, tup); + +@@ -543,13 +575,15 @@ AddNewAttributeTuples(Oid new_rel_oid, + TupleDesc tupdesc, + char relkind, + bool oidislocal, +- int oidinhcount) ++ int oidinhcount, ++ Oid *secLabels) + { + Form_pg_attribute attr; + int i; + Relation rel; + CatalogIndexState indstate; + int natts = tupdesc->natts; ++ Oid new_att_secid; + ObjectAddress myself, + referenced; + +@@ -573,7 +607,11 @@ AddNewAttributeTuples(Oid new_rel_oid, + attr->attstattarget = -1; + attr->attcacheoff = -1; + +- InsertPgAttributeTuple(rel, attr, indstate); ++ /* Security label of the column */ ++ new_att_secid = (!secLabels ? InvalidOid ++ : secLabels[i - FirstLowInvalidHeapAttributeNumber]); ++ ++ InsertPgAttributeTuple(rel, attr, indstate, new_att_secid); + + /* Add dependency info */ + myself.classId = RelationRelationId; +@@ -601,6 +639,12 @@ AddNewAttributeTuples(Oid new_rel_oid, + SysAtt[i]->attnum == ObjectIdAttributeNumber) + continue; + ++ /* skip Secid where appropriate */ ++ if (SysAtt[i]->attnum == SecurityAttributeNumber && ++ (relkind != RELKIND_RELATION || ++ new_rel_oid == SecurityRelationId)) ++ continue; ++ + memcpy(&attStruct, (char *) SysAtt[i], sizeof(FormData_pg_attribute)); + + /* Fill in the correct relation OID in the copied tuple */ +@@ -613,7 +657,11 @@ AddNewAttributeTuples(Oid new_rel_oid, + attStruct.attinhcount = oidinhcount; + } + +- InsertPgAttributeTuple(rel, &attStruct, indstate); ++ /* Security label of the system column */ ++ new_att_secid = (!secLabels ? InvalidOid ++ : secLabels[SysAtt[i]->attnum - FirstLowInvalidHeapAttributeNumber]); ++ ++ InsertPgAttributeTuple(rel, &attStruct, indstate, new_att_secid); + } + } + +@@ -641,7 +689,8 @@ void + InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, +- Datum reloptions) ++ Datum reloptions, ++ Oid new_rel_secid) + { + Form_pg_class rd_rel = new_rel_desc->rd_rel; + Datum values[Natts_pg_class]; +@@ -690,6 +739,9 @@ InsertPgClassTuple(Relation pg_class_desc, + */ + HeapTupleSetOid(tup, new_rel_oid); + ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, new_rel_secid); ++ + /* finally insert the new tuple, update the indexes, and clean up */ + simple_heap_insert(pg_class_desc, tup); + +@@ -712,9 +764,11 @@ AddNewRelationTuple(Relation pg_class_desc, + Oid new_type_oid, + Oid relowner, + char relkind, +- Datum reloptions) ++ Datum reloptions, ++ Oid *secLabels) + { + Form_pg_class new_rel_reltup; ++ Oid new_rel_secid = InvalidOid; + + /* + * first we update some of the information in our uncataloged relation's +@@ -771,8 +825,12 @@ AddNewRelationTuple(Relation pg_class_desc, + + new_rel_desc->rd_att->tdtypeid = new_type_oid; + ++ if (secLabels) ++ new_rel_secid = secLabels[0]; ++ + /* Now build and insert the tuple */ +- InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions); ++ InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, ++ reloptions, new_rel_secid); + } + + +@@ -843,7 +901,8 @@ heap_create_with_catalog(const char *relname, + int oidinhcount, + OnCommitAction oncommit, + Datum reloptions, +- bool allow_system_table_mods) ++ bool allow_system_table_mods, ++ Oid *secLabels) + { + Relation pg_class_desc; + Relation new_rel_desc; +@@ -1019,13 +1078,14 @@ heap_create_with_catalog(const char *relname, + new_type_oid, + ownerid, + relkind, +- reloptions); ++ reloptions, ++ secLabels); + + /* + * now add tuples to pg_attribute for the attributes in our new relation. + */ + AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, +- oidislocal, oidinhcount); ++ oidislocal, oidinhcount, secLabels); + + /* + * Make a dependency link to force the relation to be deleted if its +@@ -1484,6 +1544,11 @@ heap_drop_with_catalog(Oid relid) + * delete relation tuple + */ + DeleteRelationTuple(relid); ++ ++ /* ++ * delete orphan pg_security entries ++ */ ++ securityReclaimOnDropTable(relid); + } + + +diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c +index ad679ea..828d51a 100644 +--- a/src/backend/catalog/index.c ++++ b/src/backend/catalog/index.c +@@ -48,6 +48,7 @@ + #include "nodes/nodeFuncs.h" + #include "optimizer/clauses.h" + #include "optimizer/var.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/procarray.h" +@@ -352,7 +353,8 @@ AppendAttributeTuples(Relation indexRelation, int numatts) + Assert(indexTupDesc->attrs[i]->attnum == i + 1); + Assert(indexTupDesc->attrs[i]->attcacheoff == -1); + +- InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate); ++ InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], ++ indstate, InvalidOid); + } + + CatalogCloseIndexes(indstate); +@@ -653,7 +655,7 @@ index_create(Oid heapRelationId, + */ + InsertPgClassTuple(pg_class, indexRelation, + RelationGetRelid(indexRelation), +- reloptions); ++ reloptions, InvalidOid); + + /* done with pg_class */ + heap_close(pg_class, RowExclusiveLock); +diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c +index 2b0cb35..64de050 100644 +--- a/src/backend/catalog/namespace.c ++++ b/src/backend/catalog/namespace.c +@@ -39,6 +39,7 @@ + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" ++#include "security/sepgsql.h" + #include "storage/backendid.h" + #include "storage/ipc.h" + #include "utils/acl.h" +@@ -2105,7 +2106,10 @@ LookupExplicitNamespace(const char *nspname) + if (strcmp(nspname, "pg_temp") == 0) + { + if (OidIsValid(myTempNamespace)) ++ { ++ sepgsql_schema_search(myTempNamespace, true); + return myTempNamespace; ++ } + + /* + * Since this is used only for looking up existing objects, there is +@@ -2127,6 +2131,7 @@ LookupExplicitNamespace(const char *nspname) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + nspname); ++ sepgsql_schema_search(namespaceId, true); + + return namespaceId; + } +@@ -2722,7 +2727,8 @@ recomputeNamespacePath(void) + if (OidIsValid(namespaceId) && + !list_member_oid(oidlist, namespaceId) && + pg_namespace_aclcheck(namespaceId, roleid, +- ACL_USAGE) == ACLCHECK_OK) ++ ACL_USAGE) == ACLCHECK_OK && ++ sepgsql_schema_search(namespaceId, false)) + oidlist = lappend_oid(oidlist, namespaceId); + } + } +@@ -2731,7 +2737,8 @@ recomputeNamespacePath(void) + /* pg_temp --- substitute temp namespace, if any */ + if (OidIsValid(myTempNamespace)) + { +- if (!list_member_oid(oidlist, myTempNamespace)) ++ if (!list_member_oid(oidlist, myTempNamespace) && ++ sepgsql_schema_search(myTempNamespace, false)) + oidlist = lappend_oid(oidlist, myTempNamespace); + } + else +@@ -2750,7 +2757,8 @@ recomputeNamespacePath(void) + if (OidIsValid(namespaceId) && + !list_member_oid(oidlist, namespaceId) && + pg_namespace_aclcheck(namespaceId, roleid, +- ACL_USAGE) == ACLCHECK_OK) ++ ACL_USAGE) == ACLCHECK_OK && ++ sepgsql_schema_search(namespaceId, false)) + oidlist = lappend_oid(oidlist, namespaceId); + } + } +@@ -2816,6 +2824,7 @@ InitTempTableNamespace(void) + char namespaceName[NAMEDATALEN]; + Oid namespaceId; + Oid toastspaceId; ++ Oid nspsecid; + + Assert(!OidIsValid(myTempNamespace)); + +@@ -2836,6 +2845,9 @@ InitTempTableNamespace(void) + errmsg("permission denied to create temporary tables in database \"%s\"", + get_database_name(MyDatabaseId)))); + ++ /* SELinux checks permission to create temp schema */ ++ nspsecid = sepgsql_schema_create(namespaceName, true, NULL); ++ + snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); + + namespaceId = GetSysCacheOid(NAMESPACENAME, +@@ -2851,7 +2863,9 @@ InitTempTableNamespace(void) + * temp tables. This works because the places that access the temp + * namespace for my own backend skip permissions checks on it. + */ +- namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); ++ namespaceId = NamespaceCreate(namespaceName, ++ BOOTSTRAP_SUPERUSERID, ++ nspsecid); + /* Advance command counter to make namespace visible */ + CommandCounterIncrement(); + } +@@ -2877,7 +2891,9 @@ InitTempTableNamespace(void) + 0, 0, 0); + if (!OidIsValid(toastspaceId)) + { +- toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); ++ toastspaceId = NamespaceCreate(namespaceName, ++ BOOTSTRAP_SUPERUSERID, ++ nspsecid); + /* Advance command counter to make namespace visible */ + CommandCounterIncrement(); + } +@@ -3030,6 +3046,13 @@ RemoveTempRelations(Oid tempNamespaceId) + object.objectId = tempNamespaceId; + object.objectSubId = 0; + ++ /* ++ * TODO: ++ * SELinux should not check db_xxx:{drop} permission during cleaning ++ * up all the temporary objects. It may be necessary a bool argument ++ * to control MAC permission check on deleteOneObject() called from ++ * deleteWhatDependsOn() and so on. ++ */ + deleteWhatDependsOn(&object, false); + } + +diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c +index 845322e..cc1c59a 100644 +--- a/src/backend/catalog/pg_aggregate.c ++++ b/src/backend/catalog/pg_aggregate.c +@@ -231,7 +231,8 @@ AggregateCreate(const char *aggName, + NIL, /* parameterDefaults */ + PointerGetDatum(NULL), /* proconfig */ + 1, /* procost */ +- 0); /* prorows */ ++ 0, /* prorows */ ++ NULL); /* proseclabel*/ + + /* + * Okay to create the pg_aggregate entry. +diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c +index d4a8183..30f2604 100644 +--- a/src/backend/catalog/pg_conversion.c ++++ b/src/backend/catalog/pg_conversion.c +@@ -40,7 +40,7 @@ Oid + ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +- Oid conproc, bool def) ++ Oid conproc, Oid consecid, bool def) + { + int i; + Relation rel; +@@ -104,6 +104,8 @@ ConversionCreate(const char *conname, Oid connamespace, + values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def); + + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, consecid); + + /* insert a new tuple */ + oid = simple_heap_insert(rel, tup); +diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c +index 54d992f..67faa30 100644 +--- a/src/backend/catalog/pg_largeobject.c ++++ b/src/backend/catalog/pg_largeobject.c +@@ -25,6 +25,7 @@ + #include "catalog/pg_largeobject_metadata.h" + #include "catalog/toasting.h" + #include "miscadmin.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -40,7 +41,7 @@ + * will appear to exist with size 0. + */ + Oid +-LargeObjectCreate(Oid loid) ++LargeObjectCreate(Oid loid, Oid secid) + { + Relation pg_lo_meta; + HeapTuple ntup; +@@ -65,6 +66,8 @@ LargeObjectCreate(Oid loid) + values, nulls); + if (OidIsValid(loid)) + HeapTupleSetOid(ntup, loid); ++ if (HeapTupleHasSecid(ntup)) ++ HeapTupleSetSecid(ntup, secid); + + loid_new = simple_heap_insert(pg_lo_meta, ntup); + Assert(!OidIsValid(loid) || loid == loid_new); +@@ -205,6 +208,9 @@ LargeObjectAlterOwner(Oid loid, Oid newOwnerId) + + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), newOwnerId); ++ ++ /* SELinux: db_blob:{setattr} */ ++ sepgsql_largeobject_alter(loid); + } + + memset(values, 0, sizeof(values)); +diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c +index 8fac0b6..62802a9 100644 +--- a/src/backend/catalog/pg_namespace.c ++++ b/src/backend/catalog/pg_namespace.c +@@ -28,7 +28,7 @@ + * --------------- + */ + Oid +-NamespaceCreate(const char *nspName, Oid ownerId) ++NamespaceCreate(const char *nspName, Oid ownerId, Oid nspsecid) + { + Relation nspdesc; + HeapTuple tup; +@@ -66,6 +66,8 @@ NamespaceCreate(const char *nspName, Oid ownerId) + tupDesc = nspdesc->rd_att; + + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, nspsecid); + + nspoid = simple_heap_insert(nspdesc, tup); + Assert(OidIsValid(nspoid)); +diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c +index af307b7..6fe025c 100644 +--- a/src/backend/catalog/pg_operator.c ++++ b/src/backend/catalog/pg_operator.c +@@ -28,6 +28,7 @@ + #include "catalog/pg_type.h" + #include "miscadmin.h" + #include "parser/parse_oper.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -204,6 +205,7 @@ OperatorShellMake(const char *operatorName, + { + Relation pg_operator_desc; + Oid operatorObjectId; ++ Oid secid; + int i; + HeapTuple tup; + Datum values[Natts_pg_operator]; +@@ -220,6 +222,10 @@ OperatorShellMake(const char *operatorName, + errmsg("\"%s\" is not a valid operator name", + operatorName))); + ++ /* SELinux permission check */ ++ secid = sepgsql_operator_create(operatorName, InvalidOid, ++ operatorNamespace, ++ InvalidOid, InvalidOid, InvalidOid); + /* + * initialize our *nulls and *values arrays + */ +@@ -260,6 +266,8 @@ OperatorShellMake(const char *operatorName, + * create a new operator tuple + */ + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup) && OidIsValid(secid)) ++ HeapTupleSetSecid(tup, secid); + + /* + * insert our "shell" operator tuple +@@ -347,6 +355,7 @@ OperatorCreate(const char *operatorName, + bool selfCommutator = false; + NameData oname; + TupleDesc tupDesc; ++ Oid secid; + int i; + + /* +@@ -476,6 +485,10 @@ OperatorCreate(const char *operatorName, + else + negatorId = InvalidOid; + ++ /* SELinux permission checks */ ++ secid = sepgsql_operator_create(operatorName, operatorObjectId, ++ operatorNamespace, ++ procedureId, restrictionId, joinId); + /* + * set up values in the operator tuple + */ +@@ -523,6 +536,8 @@ OperatorCreate(const char *operatorName, + values, + nulls, + replaces); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + simple_heap_update(pg_operator_desc, &tup->t_self, tup); + } +@@ -530,6 +545,8 @@ OperatorCreate(const char *operatorName, + { + tupDesc = pg_operator_desc->rd_att; + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + operatorObjectId = simple_heap_insert(pg_operator_desc, tup); + } +diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c +index 2980d2b..55e66e5 100644 +--- a/src/backend/catalog/pg_proc.c ++++ b/src/backend/catalog/pg_proc.c +@@ -29,6 +29,7 @@ + #include "miscadmin.h" + #include "nodes/nodeFuncs.h" + #include "parser/parse_type.h" ++#include "security/sepgsql.h" + #include "tcop/pquery.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" +@@ -78,7 +79,8 @@ ProcedureCreate(const char *procedureName, + List *parameterDefaults, + Datum proconfig, + float4 procost, +- float4 prorows) ++ float4 prorows, ++ Node *proseclabel) + { + Oid retval; + int parameterCount; +@@ -97,6 +99,7 @@ ProcedureCreate(const char *procedureName, + Datum values[Natts_pg_proc]; + bool replaces[Natts_pg_proc]; + Oid relid; ++ Oid prosecid = InvalidOid; + NameData procname; + TupleDesc tupDesc; + bool is_update; +@@ -344,6 +347,11 @@ ProcedureCreate(const char *procedureName, + ObjectIdGetDatum(procNamespace), + 0); + ++ /* Check permission to create/replace a function */ ++ prosecid = sepgsql_proc_create(procedureName, oldtup, ++ procNamespace, languageObjectId, ++ (DefElem *)proseclabel); ++ + if (HeapTupleIsValid(oldtup)) + { + /* There is one; okay to replace it? */ +@@ -481,6 +489,8 @@ ProcedureCreate(const char *procedureName, + + /* Okay, do it... */ + tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, prosecid); + simple_heap_update(rel, &tup->t_self, tup); + + ReleaseSysCache(oldtup); +@@ -490,6 +500,8 @@ ProcedureCreate(const char *procedureName, + { + /* Creating a new procedure */ + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, prosecid); + simple_heap_insert(rel, tup); + is_update = false; + } +diff --git a/src/backend/catalog/pg_security.c b/src/backend/catalog/pg_security.c +new file mode 100644 +index 0000000..0db05e2 +--- /dev/null ++++ b/src/backend/catalog/pg_security.c +@@ -0,0 +1,483 @@ ++/* ++ * src/backend/catalog/pg_security.c ++ * routines to support security label management ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++ ++#include "access/genam.h" ++#include "access/heapam.h" ++#include "access/sysattr.h" ++#include "access/xact.h" ++#include "catalog/catalog.h" ++#include "catalog/indexing.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_security.h" ++#include "catalog/pg_type.h" ++#include "executor/spi.h" ++#include "miscadmin.h" ++#include "security/rowlevel.h" ++#include "security/sepgsql.h" ++#include "utils/builtins.h" ++#include "utils/fmgroids.h" ++#include "utils/memutils.h" ++#include "utils/rel.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++ ++bool ++securityTupleDescHasSecid(Oid relid, char relkind) ++{ ++ return sepgsqlTupleDescHasSecid(relid, relkind); ++} ++ ++/* ++ * securityOnCreateDatabase ++ * copies all the entries refered by source database ++ */ ++void ++securityOnCreateDatabase(Oid src_datid, Oid dst_datid) ++{ ++ Relation rel; ++ ScanKeyData keys[1]; ++ SysScanDesc scan; ++ HeapTuple oldtup, newtup; ++ Datum values[Natts_pg_security]; ++ bool nulls[Natts_pg_security]; ++ bool replaces[Natts_pg_security]; ++ ++ /* Scan all entries with pg_security.datid = src_datid */ ++ ScanKeyInit(&keys[0], ++ Anum_pg_security_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(src_datid)); ++ ++ rel = heap_open(SecurityRelationId, RowExclusiveLock); ++ ++ scan = systable_beginscan(rel, SecuritySecidIndexId, true, ++ SnapshotNow, 1, keys); ++ ++ /* pg_security.datid shall be replaced */ ++ memset(values, 0, sizeof(values)); ++ memset(nulls, false, sizeof(nulls)); ++ memset(replaces, false, sizeof(replaces)); ++ ++ values[Anum_pg_security_datid - 1] = ObjectIdGetDatum(dst_datid); ++ replaces[Anum_pg_security_datid - 1] = true; ++ ++ while (HeapTupleIsValid(oldtup = systable_getnext(scan))) ++ { ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ values, nulls, replaces); ++ simple_heap_insert(rel, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ } ++ systable_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++/* ++ * securityOnDropDatabase ++ * drops all the entries refered by dropped database ++ */ ++void ++securityOnDropDatabase(Oid datid) ++{ ++ Relation rel; ++ ScanKeyData keys[1]; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ ++ /* Scan all entries with pg_security.datid = datid */ ++ ScanKeyInit(&keys[0], ++ Anum_pg_security_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(datid)); ++ ++ rel = heap_open(SecurityRelationId, RowExclusiveLock); ++ ++ scan = systable_beginscan(rel, SecuritySecidIndexId, true, ++ SnapshotNow, 1, keys); ++ ++ while (HeapTupleIsValid(tuple = systable_getnext(scan))) ++ { ++ simple_heap_delete(rel, &tuple->t_self); ++ } ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++/* ++ * InputSecurityAttr ++ */ ++static Oid ++InputSecurityAttr(Oid relid, const char *secattr) ++{ ++ LOCKMODE lockmode = AccessShareLock; ++ Relation rel; ++ ScanKeyData skey[3]; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ Oid datid; ++ Oid secid; ++ Datum values[Natts_pg_security]; ++ bool nulls[Natts_pg_security]; ++ ++ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); ++ ++retry: ++ /* ++ * Lookup pg_security catalog first ++ */ ++ rel = heap_open(SecurityRelationId, lockmode); ++ ++ ScanKeyInit(&skey[0], ++ Anum_pg_security_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(datid)); ++ ScanKeyInit(&skey[1], ++ Anum_pg_security_relid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relid)); ++ ScanKeyInit(&skey[2], ++ Anum_pg_security_secattr, ++ BTEqualStrategyNumber, F_TEXTEQ, ++ CStringGetTextDatum(secattr)); ++ ++ scan = systable_beginscan(rel, SecuritySecattrIndexId, true, ++ SnapshotToast, 3, skey); ++ ++ tuple = systable_getnext(scan); ++ if (HeapTupleIsValid(tuple)) ++ { ++ secid = ((Form_pg_security) GETSTRUCT(tuple))->secid; ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, lockmode); ++ ++ return secid; ++ } ++ ++ systable_endscan(scan); ++ ++ /* ++ * If not exist, try to insert a new entry. ++ */ ++ if (lockmode == AccessShareLock) ++ { ++ heap_close(rel, lockmode); ++ ++ lockmode = RowExclusiveLock; ++ ++ goto retry; ++ } ++ ++ memset(nulls, false, sizeof(nulls)); ++ secid = GetNewOidWithIndex(rel, SecuritySecidIndexId, ++ Anum_pg_security_secid); ++ values[Anum_pg_security_secid - 1] = ObjectIdGetDatum(secid); ++ values[Anum_pg_security_datid - 1] = ObjectIdGetDatum(datid); ++ values[Anum_pg_security_relid - 1] = ObjectIdGetDatum(relid); ++ values[Anum_pg_security_secattr - 1] = CStringGetTextDatum(secattr); ++ ++ tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); ++ ++ simple_heap_insert(rel, tuple); ++ ++ CatalogUpdateIndexes(rel, tuple); ++ ++ heap_close(rel, lockmode); ++ ++ return secid; ++} ++ ++static char * ++OutputSecurityAttr(Oid relid, Oid secid) ++{ ++ Relation rel; ++ ScanKeyData skey[3]; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ Oid datid; ++ char *result = NULL; ++ ++ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); ++ ++ /* ++ * Lookup pg_security catalog first ++ */ ++ rel = heap_open(SecurityRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey[0], ++ Anum_pg_security_secid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(secid)); ++ ScanKeyInit(&skey[1], ++ Anum_pg_security_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(datid)); ++ ScanKeyInit(&skey[2], ++ Anum_pg_security_relid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relid)); ++ ++ scan = systable_beginscan(rel, SecuritySecidIndexId, true, ++ SnapshotToast, 3, skey); ++ ++ tuple = systable_getnext(scan); ++ if (HeapTupleIsValid(tuple)) ++ { ++ Datum datum; ++ bool isnull; ++ ++ datum = heap_getattr(tuple, ++ Anum_pg_security_secattr, ++ RelationGetDescr(rel), &isnull); ++ if (!isnull) ++ result = TextDatumGetCString(datum); ++ } ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, AccessShareLock); ++ ++ return result; ++} ++ ++/* ++ * input/output handler ++ */ ++Oid ++securityRawSecLabelIn(Oid relid, char *seclabel) ++{ ++ seclabel = sepgsqlRawSecLabelIn(seclabel); ++ ++ return InputSecurityAttr(relid, seclabel); ++} ++ ++char * ++securityRawSecLabelOut(Oid relid, Oid secid) ++{ ++ char *seclabel = OutputSecurityAttr(relid, secid); ++ ++ return sepgsqlRawSecLabelOut(seclabel); ++} ++ ++Oid ++securityTransSecLabelIn(Oid relid, char *seclabel) ++{ ++ seclabel = sepgsqlTransSecLabelIn(seclabel); ++ ++ return securityRawSecLabelIn(relid, seclabel); ++} ++ ++char * ++securityTransSecLabelOut(Oid relid, Oid secid) ++{ ++ char *seclabel = securityRawSecLabelOut(relid, secid); ++ ++ return sepgsqlTransSecLabelOut(seclabel); ++} ++ ++/* ++ * Output handler for system columns ++ */ ++Datum ++securitySysattSecLabelOut(Oid relid, HeapTuple tuple) ++{ ++ char *seclabel; ++ ++ seclabel = sepgsqlSysattSecLabelOut(relid, tuple); ++ if (!seclabel) ++ seclabel = "unlabled"; ++ ++ return CStringGetTextDatum(seclabel); ++} ++ ++/* ++ * securityReclaimOnDropTable ++ * drop orphan entries within pg_security on drop table ++ */ ++void ++securityReclaimOnDropTable(Oid relid) ++{ ++ Relation rel; ++ SysScanDesc scan; ++ ScanKeyData key[2]; ++ HeapTuple tuple; ++ Oid database_oid; ++ ++ database_oid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); ++ ScanKeyInit(&key[0], ++ Anum_pg_security_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(database_oid)); ++ ScanKeyInit(&key[1], ++ Anum_pg_security_relid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relid)); ++ ++ rel = heap_open(SecurityRelationId, RowExclusiveLock); ++ scan = systable_beginscan(rel, SecuritySecattrIndexId, true, ++ SnapshotNow, 2, key); ++ while (HeapTupleIsValid(tuple = systable_getnext(scan))) ++ simple_heap_delete(rel, &tuple->t_self); ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++/* ++ * security_quote_relation ++ * returns palloc'de identifier with explicit namespace ++ */ ++static char * ++security_quote_relation(Oid relid) ++{ ++ Oid nspoid = get_rel_namespace(relid); ++ char *nspname; ++ char *relname; ++ ++ nspname = get_namespace_name(nspoid); ++ relname = get_rel_name(relid); ++ ++ return quote_qualified_identifier(nspname, relname); ++} ++ ++/* ++ * security_reclaim_table ++ * reclaims orphan entries associated to a certain table ++ */ ++static int ++seclabelRelationReclaimExec(Oid relOid) ++{ ++ StringInfoData query; ++ SPIPlanPtr plan; ++ Oid types[2]; ++ Datum values[2]; ++ Oid proc_oid; ++ Oid database_oid; ++ char *relname_full; ++ char *attname_datid; ++ char *attname_relid; ++ char *attname_secid; ++ char *attname_seckind; ++ char *attname_secattr; ++ char *sec_proname; ++ char *sec_nspname; ++ Form_pg_proc proForm; ++ HeapTuple protup; ++ ++ /* ++ * LOCK the target table ++ */ ++ initStringInfo(&query); ++ relname_full = security_quote_relation(relOid); ++ appendStringInfo(&query, "LOCK %s IN SHARE MODE", relname_full); ++ if (SPI_execute(query.data, false, 0) != SPI_OK_UTILITY) ++ elog(ERROR, "SPI_execute failed on %s", query.data); ++ ++ /* ++ * DELETE orphan entries ++ */ ++ initStringInfo(&query); ++ attname_secid = get_attname(SecurityRelationId, Anum_pg_security_secid); ++ attname_datid = get_attname(SecurityRelationId, Anum_pg_security_datid); ++ attname_relid = get_attname(SecurityRelationId, Anum_pg_security_relid); ++ attname_secattr = get_attname(SecurityRelationId, Anum_pg_security_secattr); ++ ++ appendStringInfo(&query, ++ "DELETE FROM %s " ++ "WHERE %s = $1 AND %s = $2 AND %s NOT IN ", ++ security_quote_relation(SecurityRelationId), ++ quote_identifier(attname_datid), ++ quote_identifier(attname_relid), ++ quote_identifier(attname_secid)); ++ ++ protup = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(F_SECLABEL_TO_SECID), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(protup)) ++ elog(ERROR, "cache lookup failed for procedure: %u", F_SECLABEL_TO_SECID); ++ ++ proForm = (Form_pg_proc) GETSTRUCT(protup); ++ sec_proname = NameStr(proForm->proname); ++ sec_nspname = get_namespace_name(proForm->pronamespace); ++ ++ appendStringInfo(&query, ++ "(SELECT %s.%s(%s) FROM ONLY %s)", ++ quote_identifier(sec_nspname), ++ quote_identifier(sec_proname), ++ quote_identifier(get_rel_name(relOid)), ++ relname_full); ++ ReleaseSysCache(protup); ++ ++ /* ++ * Setup and execute query ++ */ ++ types[0] = OIDOID; ++ types[1] = OIDOID; ++ plan = SPI_prepare(query.data, 2, types); ++ if (!plan) ++ elog(ERROR, "SPI_prepare failed on %s", query.data); ++ ++ database_oid = (IsSharedRelation(relOid) ? InvalidOid : MyDatabaseId); ++ ++ values[0] = ObjectIdGetDatum(database_oid); ++ values[1] = ObjectIdGetDatum(relOid); ++ if (SPI_execute_plan(plan, values, NULL, false, 0) != SPI_OK_DELETE) ++ elog(ERROR, "SPI_execute_plan failed on %s", query.data); ++ ++ SPI_freetuptable(SPI_tuptable); ++ ++ return SPI_processed; ++} ++ ++void ++seclabelRelationReclaim(Oid relOid) ++{ ++ int save_mode; ++ ++ if (!superuser() || ++ get_rel_relkind(relOid) != RELKIND_RELATION) ++ return; ++ ++ save_mode = sepostgresql_mode; ++ sepostgresql_mode = SEPGSQL_MODE_INTERNAL; ++ PG_TRY(); ++ { ++ if (SPI_connect() != SPI_OK_CONNECT) ++ elog(ERROR, "SPI_connect failed"); ++ ++ seclabelRelationReclaimExec(relOid); ++ ++ if (SPI_finish() != SPI_OK_FINISH) ++ elog(ERROR, "SPI_finish failed"); ++ } ++ PG_CATCH(); ++ { ++ sepostgresql_mode = save_mode; ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ sepostgresql_mode = save_mode; ++} ++ ++Datum ++seclabel_to_secid(PG_FUNCTION_ARGS) ++{ ++ HeapTupleHeader tuphdr = PG_GETARG_HEAPTUPLEHEADER(0); ++ ++ PG_RETURN_OID(HeapTupleHeaderGetSecid(tuphdr)); ++} +diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c +index 451724f..b95b414 100644 +--- a/src/backend/catalog/pg_shdepend.c ++++ b/src/backend/catalog/pg_shdepend.c +@@ -37,6 +37,7 @@ + #include "commands/schemacmds.h" + #include "commands/tablecmds.h" + #include "commands/typecmds.h" ++#include "security/sepgsql.h" + #include "storage/lmgr.h" + #include "miscadmin.h" + #include "utils/acl.h" +@@ -1340,6 +1341,8 @@ shdepReassignOwned(List *roleids, Oid newrole) + break; + + case TypeRelationId: ++ /* SELinux checks */ ++ sepgsql_type_alter(sdepForm->objid, NULL, InvalidOid); + AlterTypeOwnerInternal(sdepForm->objid, newrole, true); + break; + +@@ -1352,7 +1355,8 @@ shdepReassignOwned(List *roleids, Oid newrole) + break; + + case RelationRelationId: +- ++ /* SELinux checks */ ++ sepgsql_relation_alter(sdepForm->objid, NULL, InvalidOid); + /* + * Pass recursing = true so that we don't fail on indexes, + * owned sequences, etc when we happen to visit them +diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c +index 7696480..4586056 100644 +--- a/src/backend/catalog/pg_type.c ++++ b/src/backend/catalog/pg_type.c +@@ -25,6 +25,7 @@ + #include "commands/typecmds.h" + #include "miscadmin.h" + #include "parser/scansup.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -56,10 +57,17 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) + Datum values[Natts_pg_type]; + bool nulls[Natts_pg_type]; + Oid typoid; ++ Oid typsid; + NameData name; + + Assert(PointerIsValid(typeName)); + ++ /* SELinux check permission to create a shell type */ ++ typsid = sepgsql_type_create(typeName, InvalidOid, typeNamespace, ++ F_SHELL_IN, F_SHELL_OUT, ++ InvalidOid, InvalidOid, ++ InvalidOid, InvalidOid, InvalidOid); ++ + /* + * open pg_type + */ +@@ -201,6 +209,7 @@ TypeCreate(Oid newTypeOid, + { + Relation pg_type_desc; + Oid typeObjectId; ++ Oid typeSecid = InvalidOid; + bool rebuildDeps = false; + HeapTuple tup; + bool nulls[Natts_pg_type]; +@@ -367,6 +376,15 @@ TypeCreate(Oid newTypeOid, + CStringGetDatum(typeName), + ObjectIdGetDatum(typeNamespace), + 0, 0); ++ ++ /* SELinux checks to create/replace type */ ++ if (!isImplicitArray && typeType != TYPTYPE_COMPOSITE) ++ typeSecid = sepgsql_type_create(typeName, tup, typeNamespace, ++ inputProcedure, outputProcedure, ++ receiveProcedure, sendProcedure, ++ typmodinProcedure, typmodoutProcedure, ++ analyzeProcedure); ++ + if (HeapTupleIsValid(tup)) + { + /* +@@ -412,6 +430,8 @@ TypeCreate(Oid newTypeOid, + /* Force the OID if requested by caller, else heap_insert does it */ + if (OidIsValid(newTypeOid)) + HeapTupleSetOid(tup, newTypeOid); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, typeSecid); + + typeObjectId = simple_heap_insert(pg_type_desc, tup); + } +diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c +index 6e7b5cf..10ea3a2 100644 +--- a/src/backend/catalog/toasting.c ++++ b/src/backend/catalog/toasting.c +@@ -28,6 +28,7 @@ + #include "catalog/toasting.h" + #include "miscadmin.h" + #include "nodes/makefuncs.h" ++#include "security/sepgsql.h" + #include "utils/builtins.h" + #include "utils/syscache.h" + +@@ -125,6 +126,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, + char toast_relname[NAMEDATALEN]; + char toast_idxname[NAMEDATALEN]; + IndexInfo *indexInfo; ++ Oid *secLabels; + Oid classObjectId[2]; + int16 coloptions[2]; + ObjectAddress baseobject, +@@ -199,6 +201,11 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, + else + namespaceid = PG_TOAST_NAMESPACE; + ++ secLabels = sepgsql_relation_create(toast_relname, ++ RELKIND_TOASTVALUE, ++ tupdesc, namespaceid, ++ NULL, NIL, false, false); ++ + toast_relid = heap_create_with_catalog(toast_relname, + namespaceid, + rel->rd_rel->reltablespace, +@@ -212,7 +219,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, + 0, + ONCOMMIT_NOOP, + reloptions, +- true); ++ true, ++ secLabels); + + /* make the toast relation visible, else index creation will fail */ + CommandCounterIncrement(); +diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c +index fd3f336..dfca678 100644 +--- a/src/backend/commands/aggregatecmds.c ++++ b/src/backend/commands/aggregatecmds.c +@@ -32,6 +32,7 @@ + #include "miscadmin.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -311,6 +312,9 @@ RenameAggregate(List *name, List *args, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux permission checks */ ++ sepgsql_proc_alter(procOid, newname, InvalidOid); ++ + /* rename */ + namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname); + simple_heap_update(rel, &tup->t_self, tup); +diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c +index 46bc4df..79131d5 100644 +--- a/src/backend/commands/alter.c ++++ b/src/backend/commands/alter.c +@@ -289,3 +289,32 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) + (int) stmt->objectType); + } + } ++ ++void ++ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt) ++{ ++ DefElem *seclabel = (DefElem *)stmt->secLabel; ++ ++ switch (stmt->objectType) ++ { ++ case OBJECT_DATABASE: ++ AlterDatabaseSecLabel(strVal(linitial(stmt->object)), seclabel); ++ break; ++ case OBJECT_SCHEMA: ++ AlterSchemaSecLabel(strVal(linitial(stmt->object)), seclabel); ++ break; ++ case OBJECT_TABLE: ++ case OBJECT_SEQUENCE: ++ case OBJECT_COLUMN: ++ CheckRelationOwnership(stmt->relation, true); ++ AlterRelationSecLabel(stmt->relation, stmt->subname, ++ stmt->objectType, seclabel); ++ break; ++ case OBJECT_FUNCTION: ++ AlterFunctionSecLabel(stmt->object, stmt->objarg, seclabel); ++ break; ++ default: ++ elog(ERROR, "unrecognized AlterSecLabelStmt type: %d", ++ (int) stmt->objectType); ++ } ++} +diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c +index a6ba2ec..b990a33 100644 +--- a/src/backend/commands/cluster.c ++++ b/src/backend/commands/cluster.c +@@ -36,6 +36,7 @@ + #include "commands/trigger.h" + #include "commands/vacuum.h" + #include "miscadmin.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/procarray.h" + #include "utils/acl.h" +@@ -617,8 +618,9 @@ rebuild_relation(Relation OldHeap, Oid indexOid) + /* + * The new relation is local to our transaction and we know nothing + * depends on it, so DROP_RESTRICT should be OK. ++ * SELinux does not check any permissions here. + */ +- performDeletion(&object, DROP_RESTRICT); ++ performDeletionNoPerms(&object, DROP_RESTRICT); + + /* performDeletion does CommandCounterIncrement at end */ + +@@ -712,7 +714,8 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace) + 0, + ONCOMMIT_NOOP, + reloptions, +- allowSystemTableMods); ++ allowSystemTableMods, ++ sepgsql_relation_copy(OldHeap)); + + ReleaseSysCache(tuple); + +@@ -924,6 +927,10 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) + if (NewHeap->rd_rel->relhasoids) + HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); + ++ /* Preserve SID, if any */ ++ if (HeapTupleHasSecid(copiedTuple)) ++ HeapTupleSetSecid(copiedTuple, HeapTupleGetSecid(tuple)); ++ + /* The heap rewrite module does the rest */ + rewrite_heap_tuple(rwstate, tuple, copiedTuple); + +diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c +index 045ffca..97914d5 100644 +--- a/src/backend/commands/conversioncmds.c ++++ b/src/backend/commands/conversioncmds.c +@@ -24,6 +24,7 @@ + #include "mb/pg_wchar.h" + #include "miscadmin.h" + #include "parser/parse_func.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -45,6 +46,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) + int from_encoding; + int to_encoding; + Oid funcoid; ++ Oid secid; + const char *from_encoding_name = stmt->for_encoding_name; + const char *to_encoding_name = stmt->to_encoding_name; + List *func_name = stmt->func_name; +@@ -96,6 +98,9 @@ CreateConversionCommand(CreateConversionStmt *stmt) + aclcheck_error(aclresult, ACL_KIND_PROC, + NameListToString(func_name)); + ++ /* SELinux checks */ ++ secid = sepgsql_conversion_create(conversion_name, namespaceId, funcoid); ++ + /* + * Check that the conversion function is suitable for the requested source + * and target encodings. We do that by calling the function with an empty +@@ -114,7 +119,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) + * name) + */ + ConversionCreate(conversion_name, namespaceId, GetUserId(), +- from_encoding, to_encoding, funcoid, stmt->def); ++ from_encoding, to_encoding, funcoid, secid, stmt->def); + } + + /* +@@ -240,6 +245,9 @@ RenameConversion(List *name, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_conversion_alter(conversionOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -336,6 +344,8 @@ AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(convForm->connamespace)); + } ++ /* SELinux checks */ ++ sepgsql_conversion_alter(HeapTupleGetOid(tup), NULL); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c +index a151999..ee7344d 100644 +--- a/src/backend/commands/copy.c ++++ b/src/backend/commands/copy.c +@@ -21,8 +21,11 @@ + #include + + #include "access/heapam.h" ++#include "access/sysattr.h" + #include "access/xact.h" ++#include "catalog/heap.h" + #include "catalog/namespace.h" ++#include "catalog/pg_security.h" + #include "catalog/pg_type.h" + #include "commands/copy.h" + #include "commands/trigger.h" +@@ -34,6 +37,8 @@ + #include "optimizer/planner.h" + #include "parser/parse_relation.h" + #include "rewrite/rewriteHandler.h" ++#include "security/rowlevel.h" ++#include "security/sepgsql.h" + #include "storage/fd.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" +@@ -160,6 +165,10 @@ typedef struct CopyStateData + char *raw_buf; + int raw_buf_index; /* next byte to process */ + int raw_buf_len; /* total # of bytes stored */ ++ ++ /* dump/restore support for security_label */ ++ FmgrInfo seclabel_out_function; ++ bool seclabel_force_quot; + } CopyStateData; + + typedef CopyStateData *CopyState; +@@ -243,8 +252,8 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0"; + /* non-export function prototypes */ + static void DoCopyTo(CopyState cstate); + static void CopyTo(CopyState cstate); +-static void CopyOneRowTo(CopyState cstate, Oid tupleOid, +- Datum *values, bool *nulls); ++static void CopyOneRowTo(CopyState cstate, HeapTuple tuple, ++ Datum *values, bool *nulls); + static void CopyFrom(CopyState cstate); + static bool CopyReadLine(CopyState cstate); + static bool CopyReadLineText(CopyState cstate); +@@ -958,12 +967,19 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + errmsg("CSV quote character must not appear in the NULL specification"))); + + /* Disallow file COPY except to superusers. */ +- if (!pipe && !superuser()) +- ereport(ERROR, +- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), +- errmsg("must be superuser to COPY to or from a file"), +- errhint("Anyone can COPY to stdout or from stdin. " +- "psql's \\copy command also works for anyone."))); ++ if (!pipe) ++ { ++ if (!superuser()) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("must be superuser to COPY to or from a file"), ++ errhint("Anyone can COPY to stdout or from stdin. " ++ "psql's \\copy command also works for anyone."))); ++ if (is_from) ++ sepgsql_file_read(stmt->filename); ++ else ++ sepgsql_file_write(stmt->filename); ++ } + + if (stmt->relation) + { +@@ -1090,6 +1106,9 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + + num_phys_attrs = tupDesc->natts; + ++ /* SELinux: check table/column level permission */ ++ sepgsqlCheckCopyTable(cstate->rel, cstate->attnumlist, is_from); ++ + /* Convert FORCE QUOTE name list to per-column flags, check validity */ + cstate->force_quote_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool)); + if (force_quote) +@@ -1104,11 +1123,31 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + int attnum = lfirst_int(cur); + + if (!list_member_int(cstate->attnumlist, attnum)) ++ { ++ Form_pg_attribute attForm; ++ ++ if (SystemAttributeIsWritable(attnum)) ++ attForm = SystemAttributeDefinition(attnum, true); ++ else ++ attForm = tupDesc->attrs[attnum - 1]; ++ ++ Assert(attForm != NULL); ++ + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", +- NameStr(tupDesc->attrs[attnum - 1]->attname)))); +- cstate->force_quote_flags[attnum - 1] = true; ++ NameStr(attForm->attname)))); ++ } ++ ++ switch (attnum) ++ { ++ case SecurityAttributeNumber: ++ cstate->seclabel_force_quot = true; ++ break; ++ default: ++ cstate->force_quote_flags[attnum - 1] = true; ++ break; ++ } + } + } + +@@ -1126,10 +1165,23 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + int attnum = lfirst_int(cur); + + if (!list_member_int(cstate->attnumlist, attnum)) ++ { ++ Form_pg_attribute attForm; ++ ++ if (SystemAttributeIsWritable(attnum)) ++ attForm = SystemAttributeDefinition(attnum, true); ++ else ++ attForm = tupDesc->attrs[attnum - 1]; ++ ++ Assert(attForm != NULL); ++ + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", +- NameStr(tupDesc->attrs[attnum - 1]->attname)))); ++ NameStr(attForm->attname)))); ++ } ++ if (SystemAttributeIsWritable(attnum)) ++ continue; /* ignore, if specified */ + cstate->force_notnull_flags[attnum - 1] = true; + } + } +@@ -1321,16 +1373,31 @@ CopyTo(CopyState cstate) + int attnum = lfirst_int(cur); + Oid out_func_oid; + bool isvarlena; ++ FmgrInfo *out_fmgr; ++ Form_pg_attribute attForm; ++ ++ switch (attnum) ++ { ++ case SecurityAttributeNumber: ++ attForm = SystemAttributeDefinition(attnum, true); ++ out_fmgr = &cstate->seclabel_out_function; ++ break; ++ ++ default: ++ attForm = attr[attnum - 1]; ++ out_fmgr = &cstate->out_functions[attnum - 1]; ++ break; ++ } + + if (cstate->binary) +- getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, ++ getTypeBinaryOutputInfo(attForm->atttypid, + &out_func_oid, + &isvarlena); + else +- getTypeOutputInfo(attr[attnum - 1]->atttypid, ++ getTypeOutputInfo(attForm->atttypid, + &out_func_oid, + &isvarlena); +- fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); ++ fmgr_info(out_func_oid, out_fmgr); + } + + /* +@@ -1385,7 +1452,14 @@ CopyTo(CopyState cstate) + CopySendChar(cstate, cstate->delim[0]); + hdr_delim = true; + +- colname = NameStr(attr[attnum - 1]->attname); ++ if (SystemAttributeIsWritable(attnum)) ++ { ++ Form_pg_attribute attForm ++ = SystemAttributeDefinition(attnum, true); ++ colname = NameStr(attForm->attname); ++ } ++ else ++ colname = NameStr(attr[attnum - 1]->attname); + + CopyAttributeOutCSV(cstate, colname, false, + list_length(cstate->attnumlist) == 1); +@@ -1411,11 +1485,15 @@ CopyTo(CopyState cstate) + { + CHECK_FOR_INTERRUPTS(); + ++ /* check Row-level permission on the tuple */ ++ if (!rowlvCopyToTuple(cstate->rel, tuple)) ++ continue; ++ + /* Deconstruct the tuple ... faster than repeated heap_getattr */ + heap_deform_tuple(tuple, tupDesc, values, nulls); + + /* Format and send the data */ +- CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls); ++ CopyOneRowTo(cstate, tuple, values, nulls); + } + + heap_endscan(scandesc); +@@ -1441,7 +1519,8 @@ CopyTo(CopyState cstate) + * Emit one row during CopyTo(). + */ + static void +-CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) ++CopyOneRowTo(CopyState cstate, HeapTuple tuple, ++ Datum *values, bool *nulls) + { + bool need_delim = false; + FmgrInfo *out_functions = cstate->out_functions; +@@ -1461,7 +1540,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + { + /* Hack --- assume Oid is same size as int32 */ + CopySendInt32(cstate, sizeof(int32)); +- CopySendInt32(cstate, tupleOid); ++ CopySendInt32(cstate, HeapTupleGetOid(tuple)); + } + } + else +@@ -1471,7 +1550,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + if (cstate->oids) + { + string = DatumGetCString(DirectFunctionCall1(oidout, +- ObjectIdGetDatum(tupleOid))); ++ ObjectIdGetDatum(HeapTupleGetOid(tuple)))); + CopySendString(cstate, string); + need_delim = true; + } +@@ -1480,8 +1559,11 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); +- Datum value = values[attnum - 1]; +- bool isnull = nulls[attnum - 1]; ++ Oid relid; ++ Datum value; ++ bool isnull; ++ bool force_quot; ++ FmgrInfo *out_fmgr; + + if (!cstate->binary) + { +@@ -1490,6 +1572,24 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + need_delim = true; + } + ++ switch (attnum) ++ { ++ case SecurityAttributeNumber: ++ relid = RelationGetRelid(cstate->rel); ++ value = securitySysattSecLabelOut(relid, tuple); ++ isnull = false; ++ force_quot = cstate->seclabel_force_quot; ++ out_fmgr = &cstate->seclabel_out_function; ++ break; ++ ++ default: ++ value = values[attnum - 1]; ++ isnull = nulls[attnum - 1]; ++ force_quot = cstate->force_quote_flags[attnum - 1]; ++ out_fmgr = &out_functions[attnum - 1]; ++ break; ++ } ++ + if (isnull) + { + if (!cstate->binary) +@@ -1501,11 +1601,9 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + { + if (!cstate->binary) + { +- string = OutputFunctionCall(&out_functions[attnum - 1], +- value); ++ string = OutputFunctionCall(out_fmgr, value); + if (cstate->csv_mode) +- CopyAttributeOutCSV(cstate, string, +- cstate->force_quote_flags[attnum - 1], ++ CopyAttributeOutCSV(cstate, string, force_quot, + list_length(cstate->attnumlist) == 1); + else + CopyAttributeOutText(cstate, string); +@@ -1514,8 +1612,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + { + bytea *outputbytes; + +- outputbytes = SendFunctionCall(&out_functions[attnum - 1], +- value); ++ outputbytes = SendFunctionCall(out_fmgr, value); + CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); + CopySendData(cstate, VARDATA(outputbytes), + VARSIZE(outputbytes) - VARHDRSZ); +@@ -1649,8 +1746,10 @@ CopyFrom(CopyState cstate) + num_defaults; + FmgrInfo *in_functions; + FmgrInfo oid_in_function; ++ FmgrInfo seclabel_in_function; + Oid *typioparams; + Oid oid_typioparam; ++ Oid seclabel_typioparam; + int attnum; + int i; + Oid in_func_oid; +@@ -1888,6 +1987,18 @@ CopyFrom(CopyState cstate) + fmgr_info(in_func_oid, &oid_in_function); + } + ++ if (list_member_int(cstate->attnumlist, ++ SecurityAttributeNumber)) ++ { ++ if (!cstate->binary) ++ getTypeInputInfo(TEXTOID, ++ &in_func_oid, &seclabel_typioparam); ++ else ++ getTypeBinaryInputInfo(TEXTOID, ++ &in_func_oid, &seclabel_typioparam); ++ fmgr_info(in_func_oid, &seclabel_in_function); ++ } ++ + values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); + nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); + +@@ -1922,6 +2033,7 @@ CopyFrom(CopyState cstate) + { + bool skip_tuple; + Oid loaded_oid = InvalidOid; ++ Oid loaded_seclabel = InvalidOid; + + CHECK_FOR_INTERRUPTS(); + +@@ -1993,14 +2105,21 @@ CopyFrom(CopyState cstate) + /* Loop to read the user attributes on the line. */ + foreach(cur, cstate->attnumlist) + { ++ Form_pg_attribute attForm; ++ Datum dat; + int attnum = lfirst_int(cur); + int m = attnum - 1; + ++ if (SystemAttributeIsWritable(attnum)) ++ attForm = SystemAttributeDefinition(attnum, true); ++ else ++ attForm = attr[m]; ++ + if (fieldno >= fldct) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("missing data for column \"%s\"", +- NameStr(attr[m]->attname)))); ++ NameStr(attForm->attname)))); + string = field_strings[fieldno++]; + + if (cstate->csv_mode && string == NULL && +@@ -2010,14 +2129,40 @@ CopyFrom(CopyState cstate) + string = cstate->null_print; + } + +- cstate->cur_attname = NameStr(attr[m]->attname); ++ cstate->cur_attname = NameStr(attForm->attname); + cstate->cur_attval = string; +- values[m] = InputFunctionCall(&in_functions[m], +- string, +- typioparams[m], +- attr[m]->atttypmod); +- if (string != NULL) +- nulls[m] = false; ++ ++ switch (attnum) ++ { ++ case SecurityAttributeNumber: ++ if (!string) ++ break; ++ ++ dat = InputFunctionCall(&seclabel_in_function, ++ string, ++ seclabel_typioparam, ++ attForm->atttypmod); ++ loaded_seclabel ++ = securityTransSecLabelIn(RelationGetRelid(cstate->rel), ++ TextDatumGetCString(dat)); ++ break; ++ ++ default: ++ if (cstate->csv_mode && string == NULL && ++ cstate->force_notnull_flags[m]) ++ { ++ /* Go ahead and read the NULL string */ ++ string = cstate->null_print; ++ } ++ ++ values[m] = InputFunctionCall(&in_functions[m], ++ string, ++ typioparams[m], ++ attForm->atttypmod); ++ if (string != NULL) ++ nulls[m] = false; ++ break; ++ } + cstate->cur_attname = NULL; + cstate->cur_attval = NULL; + } +@@ -2063,17 +2208,41 @@ CopyFrom(CopyState cstate) + i = 0; + foreach(cur, cstate->attnumlist) + { ++ Form_pg_attribute attForm; ++ Datum dat; + int attnum = lfirst_int(cur); + int m = attnum - 1; + +- cstate->cur_attname = NameStr(attr[m]->attname); ++ if (SystemAttributeIsWritable(attnum)) ++ attForm = SystemAttributeDefinition(attnum, false); ++ else ++ attForm = attr[m]; ++ ++ cstate->cur_attname = NameStr(attForm->attname); + i++; +- values[m] = CopyReadBinaryAttribute(cstate, +- i, +- &in_functions[m], +- typioparams[m], +- attr[m]->atttypmod, +- &nulls[m]); ++ ++ switch (attnum) ++ { ++ case SecurityAttributeNumber: ++ dat = CopyReadBinaryAttribute(cstate, i, ++ &seclabel_in_function, ++ seclabel_typioparam, ++ attForm->atttypmod, ++ &isnull); ++ if (!isnull) ++ loaded_seclabel ++ = securityTransSecLabelIn(RelationGetRelid(cstate->rel), ++ TextDatumGetCString(dat)); ++ break; ++ ++ default: ++ values[m] = CopyReadBinaryAttribute(cstate, i, ++ &in_functions[m], ++ typioparams[m], ++ attr[m]->atttypmod, ++ &nulls[m]); ++ break; ++ } + cstate->cur_attname = NULL; + } + } +@@ -2094,6 +2263,8 @@ CopyFrom(CopyState cstate) + + if (cstate->oids && file_has_oids) + HeapTupleSetOid(tuple, loaded_oid); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, loaded_seclabel); + + /* Triggers and stuff need to be invoked in query context. */ + MemoryContextSwitchTo(oldcontext); +@@ -2118,6 +2289,9 @@ CopyFrom(CopyState cstate) + } + + if (!skip_tuple) ++ sepgsqlHeapTupleInsert(cstate->rel, tuple, false); ++ ++ if (!skip_tuple) + { + /* Place tuple in tuple slot */ + ExecStoreTuple(tuple, slot, InvalidBuffer, false); +@@ -3398,6 +3572,13 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) + } + if (attnum == InvalidAttrNumber) + { ++ Form_pg_attribute attForm ++ = SystemAttributeByName(name, tupDesc->tdhasoid); ++ if (attForm && SystemAttributeIsWritable(attForm->attnum)) ++ attnum = attForm->attnum; ++ } ++ if (attnum == InvalidAttrNumber) ++ { + if (rel != NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), +@@ -3445,7 +3626,8 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self) + slot_getallattrs(slot); + + /* And send the data */ +- CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); ++ CopyOneRowTo(cstate, slot->tts_tuple, ++ slot->tts_values, slot->tts_isnull); + } + + /* +diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c +index ec1db5a..ea35f05 100644 +--- a/src/backend/commands/dbcommands.c ++++ b/src/backend/commands/dbcommands.c +@@ -33,6 +33,7 @@ + #include "catalog/indexing.h" + #include "catalog/pg_authid.h" + #include "catalog/pg_database.h" ++#include "catalog/pg_security.h" + #include "catalog/pg_tablespace.h" + #include "commands/comment.h" + #include "commands/dbcommands.h" +@@ -41,6 +42,7 @@ + #include "miscadmin.h" + #include "pgstat.h" + #include "postmaster/bgwriter.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "storage/lmgr.h" +@@ -111,6 +113,7 @@ createdb(const CreatedbStmt *stmt) + bool new_record_nulls[Natts_pg_database]; + Oid dboid; + Oid datdba; ++ Oid datsecid; + ListCell *option; + DefElem *dtablespacename = NULL; + DefElem *downer = NULL; +@@ -119,6 +122,7 @@ createdb(const CreatedbStmt *stmt) + DefElem *dcollate = NULL; + DefElem *dctype = NULL; + DefElem *dconnlimit = NULL; ++ DefElem *dseclabel = NULL; + char *dbname = stmt->dbname; + char *dbowner = NULL; + const char *dbtemplate = NULL; +@@ -200,6 +204,14 @@ createdb(const CreatedbStmt *stmt) + errmsg("LOCATION is not supported anymore"), + errhint("Consider using tablespaces instead."))); + } ++ else if (strcmp(defel->defname, "security_context") == 0) ++ { ++ if (dseclabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_SYNTAX_ERROR), ++ errmsg("conflicting or redundant options"))); ++ dseclabel = defel; ++ } + else + elog(ERROR, "option \"%s\" not recognized", + defel->defname); +@@ -294,6 +306,9 @@ createdb(const CreatedbStmt *stmt) + errmsg("template database \"%s\" does not exist", + dbtemplate))); + ++ /* SELinux checks db_database:{create} */ ++ datsecid = sepgsql_database_create(dbname, src_dboid, dseclabel); ++ + /* + * Permission check: to copy a DB that's not marked datistemplate, you + * must be superuser or the owner thereof. +@@ -557,6 +572,8 @@ createdb(const CreatedbStmt *stmt) + new_record, new_record_nulls); + + HeapTupleSetOid(tuple, dboid); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, datsecid); + + simple_heap_insert(pg_database_rel, tuple); + +@@ -573,6 +590,9 @@ createdb(const CreatedbStmt *stmt) + /* Create pg_shdepend entries for objects within database */ + copyTemplateDependencies(src_dboid, dboid); + ++ /* Create pg_security entries for objects within database */ ++ securityOnCreateDatabase(src_dboid, dboid); ++ + /* + * Force a checkpoint before starting the copy. This will force dirty + * buffers out to disk, to ensure source database is up-to-date on disk +@@ -776,6 +796,9 @@ dropdb(const char *dbname, bool missing_ok) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + dbname); + ++ /* SELinux checks db_database:{drop} permission */ ++ sepgsql_database_drop(db_id); ++ + /* + * Disallow dropping a DB that is marked istemplate. This is just to + * prevent people from accidentally dropping template0 or template1; they +@@ -829,6 +852,11 @@ dropdb(const char *dbname, bool missing_ok) + dropDatabaseDependencies(db_id); + + /* ++ * Remove pg_security entries for the database. ++ */ ++ securityOnDropDatabase(db_id); ++ ++ /* + * Drop pages for this database that are in the shared buffer cache. This + * is important to ensure that no remaining backend tries to write out a + * dirty buffer to the dead database later... +@@ -913,6 +941,9 @@ RenameDatabase(const char *oldname, const char *newname) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to rename database"))); + ++ /* SELinux: check db_database:{setattr} */ ++ sepgsql_database_alter(db_id); ++ + /* + * Make sure the new name doesn't exist. See notes for same error in + * CREATE DATABASE. +@@ -1025,6 +1056,9 @@ movedb(const char *dbname, const char *tblspcname) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + dbname); + ++ /* SELinux checks db_database:{setattr} */ ++ sepgsql_database_alter(db_id); ++ + /* + * Obviously can't move the tables of my own database + */ +@@ -1377,6 +1411,9 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + stmt->dbname); + ++ /* SELinux checks db_database:{setattr} */ ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); ++ + /* + * Build an updated tuple, perusing the information just obtained + */ +@@ -1449,6 +1486,9 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + stmt->dbname); + ++ /* SELinux checks db_database:{setattr} */ ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); ++ + memset(repl_repl, false, sizeof(repl_repl)); + repl_repl[Anum_pg_database_datconfig - 1] = true; + +@@ -1571,6 +1611,9 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to change owner of database"))); + ++ /* SELinux checks db_database:{setattr} */ ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); ++ + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + +@@ -1615,6 +1658,58 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) + */ + } + ++/* ++ * ALTER DATABASE name SECURITY_LABEL [=] newlabel ++ */ ++void ++AlterDatabaseSecLabel(const char *dbname, DefElem *seclabel) ++{ ++ Relation rel; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ ScanKeyData scankey; ++ SysScanDesc scan; ++ Oid secid; ++ bool replaces[Natts_pg_database]; ++ ++ /* Fetch the old tuple */ ++ rel = heap_open(DatabaseRelationId, RowExclusiveLock); ++ ScanKeyInit(&scankey, ++ Anum_pg_database_datname, ++ BTEqualStrategyNumber, F_NAMEEQ, ++ NameGetDatum(dbname)); ++ scan = systable_beginscan(rel, DatabaseNameIndexId, true, ++ SnapshotNow, 1, &scankey); ++ oldtup = systable_getnext(scan); ++ if (!HeapTupleIsValid(oldtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_DATABASE), ++ errmsg("database \"%s\" does not exist", dbname))); ++ ++ memset(replaces, false, sizeof(replaces)); ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ NULL, NULL, replaces); ++ if (!HeapTupleHasSecid(newtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set security label on \"%s\"", dbname))); ++ systable_endscan(scan); ++ ++ /* check DAC permission */ ++ if (!pg_database_ownercheck(HeapTupleGetOid(newtup), GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); ++ ++ /* SELinux checks db_database:{setattr relabelfrom relabelto} */ ++ secid = sepgsql_database_relabel(HeapTupleGetOid(newtup), seclabel); ++ HeapTupleSetSecid(newtup, secid); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ ++ heap_close(rel, RowExclusiveLock); ++} + + /* + * Helper functions +diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c +index 46493b1..7236279 100644 +--- a/src/backend/commands/foreigncmds.c ++++ b/src/backend/commands/foreigncmds.c +@@ -27,6 +27,7 @@ + #include "foreign/foreign.h" + #include "miscadmin.h" + #include "parser/parse_func.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -233,6 +234,9 @@ AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId) + + if (form->fdwowner != newOwnerId) + { ++ /* SELinux permission check */ ++ sepgsql_fdw_alter(fdwId, InvalidOid); ++ + form->fdwowner = newOwnerId; + + simple_heap_update(rel, &tup->t_self, tup); +@@ -297,6 +301,8 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + } + } ++ /* SELinux permission checks */ ++ sepgsql_foreign_server_alter(srvId); + + form->srvowner = newOwnerId; + +@@ -342,6 +348,7 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) + Oid fdwvalidator; + Datum fdwoptions; + Oid ownerId; ++ Oid secid; + + /* Must be super user */ + if (!superuser()) +@@ -380,6 +387,9 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) + else + fdwvalidator = InvalidOid; + ++ /* SELinux permission checks */ ++ secid = sepgsql_fdw_create(stmt->fdwname, fdwvalidator); ++ + values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = fdwvalidator; + + nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true; +@@ -393,6 +403,8 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) + nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; + + tuple = heap_form_tuple(rel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, secid); + + fdwId = simple_heap_insert(rel, tuple); + CatalogUpdateIndexes(rel, tuple); +@@ -487,6 +499,9 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt) + fdwvalidator = DatumGetObjectId(datum); + } + ++ /* SELinux permission checks */ ++ sepgsql_fdw_alter(fdwId, fdwvalidator); ++ + /* + * Options specified, validate and update. + */ +@@ -609,6 +624,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt) + HeapTuple tuple; + Oid srvId; + Oid ownerId; ++ Oid secid; + AclResult aclresult; + ObjectAddress myself; + ObjectAddress referenced; +@@ -636,6 +652,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + ++ secid = sepgsql_foreign_server_create(stmt->fdwname); ++ + /* + * Insert tuple into pg_foreign_server. + */ +@@ -676,6 +694,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt) + nulls[Anum_pg_foreign_server_srvoptions - 1] = true; + + tuple = heap_form_tuple(rel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, secid); + + srvId = simple_heap_insert(rel, tuple); + +@@ -732,6 +752,9 @@ AlterForeignServer(AlterForeignServerStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, + stmt->servername); + ++ /* SELinux permission checks */ ++ sepgsql_foreign_server_alter(srvId); ++ + memset(repl_val, 0, sizeof(repl_val)); + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); +diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c +index f0989bf..0668bd7 100644 +--- a/src/backend/commands/functioncmds.c ++++ b/src/backend/commands/functioncmds.c +@@ -53,6 +53,7 @@ + #include "parser/parse_expr.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -517,7 +518,8 @@ compute_attributes_sql_style(List *options, + bool *security_definer, + ArrayType **proconfig, + float4 *procost, +- float4 *prorows) ++ float4 *prorows, ++ Node **proseclabel) + { + ListCell *option; + DefElem *as_item = NULL; +@@ -529,6 +531,7 @@ compute_attributes_sql_style(List *options, + List *set_items = NIL; + DefElem *cost_item = NULL; + DefElem *rows_item = NULL; ++ DefElem *seclabel_item = NULL; + + foreach(option, options) + { +@@ -558,6 +561,14 @@ compute_attributes_sql_style(List *options, + errmsg("conflicting or redundant options"))); + windowfunc_item = defel; + } ++ else if (strcmp(defel->defname, "security_context") == 0) ++ { ++ if (seclabel_item) ++ ereport(ERROR, ++ (errcode(ERRCODE_SYNTAX_ERROR), ++ errmsg("conflicting or redundant options"))); ++ seclabel_item = defel; ++ } + else if (compute_common_attribute(defel, + &volatility_item, + &strict_item, +@@ -622,6 +633,8 @@ compute_attributes_sql_style(List *options, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("ROWS must be positive"))); + } ++ if (seclabel_item) ++ *proseclabel = (Node *)seclabel_item; + } + + +@@ -762,6 +775,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) + ArrayType *proconfig; + float4 procost; + float4 prorows; ++ Node *proseclabel; + HeapTuple languageTuple; + Form_pg_language languageStruct; + List *as_clause; +@@ -784,13 +798,14 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) + proconfig = NULL; + procost = -1; /* indicates not set */ + prorows = -1; /* indicates not set */ ++ proseclabel = NULL; + + /* override attributes from explicit list */ + compute_attributes_sql_style(stmt->options, + &as_clause, &language, + &isWindowFunc, &volatility, + &isStrict, &security, +- &proconfig, &procost, &prorows); ++ &proconfig, &procost, &prorows, &proseclabel); + + /* Convert language name to canonical case */ + languageName = case_translate_language_name(language); +@@ -926,7 +941,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) + parameterDefaults, + PointerGetDatum(proconfig), + procost, +- prorows); ++ prorows, ++ proseclabel); + } + + +@@ -1112,6 +1128,9 @@ RenameFunction(List *name, List *argtypes, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux permission checks */ ++ sepgsql_proc_alter(procOid, newname, InvalidOid); ++ + /* rename */ + namestrcpy(&(procForm->proname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -1220,6 +1239,8 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(procForm->pronamespace)); + } ++ /* SELinux permission checks */ ++ sepgsql_proc_alter(procOid, NULL, InvalidOid); + + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); +@@ -1258,6 +1279,59 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) + } + + /* ++ * ALTER FUNCTION name(args,...) SECURITY_LABEL [=] newlabel ++ */ ++void ++AlterFunctionSecLabel(List *name, List *argtypes, DefElem *seclabel) ++{ ++ Relation rel; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ Oid procOid; ++ Oid secid; ++ bool replaces[Natts_pg_proc]; ++ ++ /* open pg_proc system catalog */ ++ rel = heap_open(ProcedureRelationId, RowExclusiveLock); ++ ++ procOid = LookupFuncNameTypeNames(name, argtypes, false); ++ ++ oldtup = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(procOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(oldtup)) ++ elog(ERROR, "cache lookup failed for function %u", procOid); ++ ++ memset(replaces, false, sizeof(replaces)); ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ NULL, NULL, replaces); ++ ++ if (!HeapTupleHasSecid(newtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set security label on \"%s\"", ++ get_func_name(procOid)))); ++ ++ ReleaseSysCache(oldtup); ++ ++ /* DAC permission checks */ ++ if (!pg_proc_ownercheck(HeapTupleGetOid(newtup), GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, ++ get_func_name(HeapTupleGetOid(newtup))); ++ ++ /* SELinux permission checks */ ++ secid = sepgsql_proc_relabel(procOid, seclabel); ++ HeapTupleSetSecid(newtup, secid); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++/* + * Implements the ALTER FUNCTION utility command (except for the + * RENAME and OWNER clauses, which are handled as part of the generic + * ALTER framework). +@@ -1296,6 +1370,9 @@ AlterFunction(AlterFunctionStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(stmt->func->funcname)); + ++ /* SELinux checks permissions */ ++ sepgsql_proc_alter(funcOid, NULL, InvalidOid); ++ + if (procForm->proisagg) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), +@@ -1473,6 +1550,7 @@ CreateCast(CreateCastStmt *stmt) + char sourcetyptype; + char targettyptype; + Oid funcid; ++ Oid secid; + int nargs; + char castcontext; + char castmethod; +@@ -1674,6 +1752,8 @@ CreateCast(CreateCastStmt *stmt) + castcontext = 0; /* keep compiler quiet */ + break; + } ++ /* SELinux permission check */ ++ secid = sepgsql_cast_create(sourcetypeid, targettypeid, funcid); + + relation = heap_open(CastRelationId, RowExclusiveLock); + +@@ -1704,6 +1784,9 @@ CreateCast(CreateCastStmt *stmt) + + tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls); + ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, secid); ++ + simple_heap_insert(relation, tuple); + + CatalogUpdateIndexes(relation, tuple); +@@ -1897,6 +1980,9 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, + NameStr(proc->proname), + newschema))); + ++ /* SELinux checks permissions */ ++ sepgsql_proc_alter(procOid, NULL, nspOid); ++ + /* OK, modify the pg_proc row */ + + /* tup is a copy, so we can scribble directly on it */ +diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c +index 99ab0e5..78b4455 100644 +--- a/src/backend/commands/indexcmds.c ++++ b/src/backend/commands/indexcmds.c +@@ -37,6 +37,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_func.h" + #include "parser/parsetree.h" ++#include "security/sepgsql.h" + #include "storage/lmgr.h" + #include "storage/proc.h" + #include "storage/procarray.h" +@@ -197,6 +198,9 @@ DefineIndex(RangeVar *heapRelation, + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceId)); ++ ++ /* SELinux checks */ ++ sepgsql_index_create(relationId, namespaceId); + } + + /* +diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c +index 1e5c92e..6949b54 100644 +--- a/src/backend/commands/lockcmds.c ++++ b/src/backend/commands/lockcmds.c +@@ -20,6 +20,7 @@ + #include "commands/lockcmds.h" + #include "miscadmin.h" + #include "parser/parse_clause.h" ++#include "security/sepgsql.h" + #include "storage/lmgr.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" +@@ -140,6 +141,9 @@ LockTableRecurse(Oid reloid, RangeVar *rv, + errmsg("\"%s\" is not a table", + RelationGetRelationName(rel)))); + ++ /* SELinux: check db_table:{lock} permission */ ++ sepgsql_relation_lock(reloid); ++ + /* + * If requested, recurse to children. We use find_inheritance_children + * not find_all_inheritors to avoid taking locks far in advance of +diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c +index 84dc2ce..b23919d 100644 +--- a/src/backend/commands/opclasscmds.c ++++ b/src/backend/commands/opclasscmds.c +@@ -35,6 +35,7 @@ + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -177,6 +178,7 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) + HeapTuple tup; + Datum values[Natts_pg_opfamily]; + bool nulls[Natts_pg_opfamily]; ++ Oid opfSecid; + NameData opfName; + ObjectAddress myself, + referenced; +@@ -197,6 +199,9 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) + errmsg("operator family \"%s\" for access method \"%s\" already exists", + opfname, amname))); + ++ /* SELinux check permission */ ++ opfSecid = sepgsql_opfamily_create(opfname, namespaceoid); ++ + /* + * Okay, let's create the pg_opfamily entry. + */ +@@ -210,6 +215,8 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) + values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId()); + + tup = heap_form_tuple(rel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, opfSecid); + + opfamilyoid = simple_heap_insert(rel, tup); + +@@ -265,6 +272,7 @@ DefineOpClass(CreateOpClassStmt *stmt) + Form_pg_am pg_am; + Datum values[Natts_pg_opclass]; + bool nulls[Natts_pg_opclass]; ++ Oid opcSecid; + AclResult aclresult; + NameData opcName; + ObjectAddress myself, +@@ -353,6 +361,9 @@ DefineOpClass(CreateOpClassStmt *stmt) + NameListToString(stmt->opfamilyname), stmt->amname))); + opfamilyoid = HeapTupleGetOid(tup); + ++ /* SELinux checks permission */ ++ sepgsql_opfamily_alter(opfamilyoid, NULL); ++ + /* + * XXX given the superuser check above, there's no need for an + * ownership check here +@@ -371,6 +382,9 @@ DefineOpClass(CreateOpClassStmt *stmt) + { + opfamilyoid = HeapTupleGetOid(tup); + ++ /* SELinux checks permission */ ++ sepgsql_opfamily_alter(opfamilyoid, NULL); ++ + /* + * XXX given the superuser check above, there's no need for an + * ownership check here +@@ -441,6 +455,8 @@ DefineOpClass(CreateOpClassStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); + #endif ++ /* SELinux check permission */ ++ sepgsql_opfamily_add_operator(opfamilyoid, operOid); + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); +@@ -465,6 +481,8 @@ DefineOpClass(CreateOpClassStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); + #endif ++ /* SELinux check permission */ ++ sepgsql_opfamily_add_procedure(opfamilyoid, funcOid); + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); +@@ -531,6 +549,9 @@ DefineOpClass(CreateOpClassStmt *stmt) + errmsg("operator class \"%s\" for access method \"%s\" already exists", + opcname, stmt->amname))); + ++ /* SELinux permission check */ ++ opcSecid = sepgsql_opclass_create(opcname, namespaceoid); ++ + /* + * If we are creating a default opclass, check there isn't one already. + * (Note we do not restrict this test to visible opclasses; this ensures +@@ -657,6 +678,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) + HeapTuple tup; + Datum values[Natts_pg_opfamily]; + bool nulls[Natts_pg_opfamily]; ++ Oid opfSecid; + AclResult aclresult; + NameData opfName; + ObjectAddress myself, +@@ -699,6 +721,9 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to create an operator family"))); + ++ /* SELinux permission check */ ++ opfSecid = sepgsql_opfamily_create(opfname, namespaceoid); ++ + rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock); + + /* +@@ -773,6 +798,7 @@ AlterOpFamily(AlterOpFamilyStmt *stmt) + int maxOpNumber, /* amstrategies value */ + maxProcNumber; /* amsupport value */ + HeapTuple tup; ++ Oid opfSecid; + Form_pg_am pg_am; + + /* Get necessary info about access method */ +@@ -805,6 +831,7 @@ AlterOpFamily(AlterOpFamilyStmt *stmt) + errmsg("operator family \"%s\" does not exist for access method \"%s\"", + NameListToString(stmt->opfamilyname), stmt->amname))); + opfamilyoid = HeapTupleGetOid(tup); ++ opfSecid = HeapTupleGetSecid(tup); + ReleaseSysCache(tup); + + /* +@@ -817,6 +844,9 @@ AlterOpFamily(AlterOpFamilyStmt *stmt) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to alter an operator family"))); + ++ /* SELinux permission checks */ ++ sepgsql_opfamily_alter(opfamilyoid, NULL); ++ + /* + * ADD and DROP cases need separate code from here on down. + */ +@@ -893,6 +923,8 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid, + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); + #endif ++ /* SELinux permission check */ ++ sepgsql_opfamily_add_operator(opfamilyoid, operOid); + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); +@@ -917,6 +949,8 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid, + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); + #endif ++ /* SELinux permission check */ ++ sepgsql_opfamily_add_procedure(opfamilyoid, funcOid); + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); +@@ -1815,6 +1849,9 @@ RenameOpClass(List *name, const char *access_method, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux permission checks */ ++ sepgsql_opclass_alter(opcOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -1915,6 +1952,9 @@ RenameOpFamily(List *name, const char *access_method, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux check permissions */ ++ sepgsql_opfamily_alter(opfOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -2035,6 +2075,8 @@ AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux permission check */ ++ sepgsql_opclass_alter(HeapTupleGetOid(tup), NULL); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +@@ -2162,6 +2204,8 @@ AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux permission checks */ ++ sepgsql_opfamily_alter(HeapTupleGetOid(tup), NULL); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c +index 6c05611..c934424 100644 +--- a/src/backend/commands/operatorcmds.c ++++ b/src/backend/commands/operatorcmds.c +@@ -45,6 +45,7 @@ + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + #include "utils/rel.h" +@@ -432,6 +433,8 @@ AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(oprForm->oprnamespace)); + } ++ /* SELinux permission check */ ++ sepgsql_operator_alter(operOid); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c +index 3faf445..8f1e212 100644 +--- a/src/backend/commands/proclang.c ++++ b/src/backend/commands/proclang.c +@@ -30,6 +30,7 @@ + #include "miscadmin.h" + #include "parser/gramparse.h" + #include "parser/parse_func.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -151,7 +152,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) + NIL, + PointerGetDatum(NULL), + 1, +- 0); ++ 0, ++ NULL); + } + + /* +@@ -186,7 +188,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) + NIL, + PointerGetDatum(NULL), + 1, +- 0); ++ 0, ++ NULL); + } + } + else +@@ -275,10 +278,16 @@ create_proc_lang(const char *languageName, + bool nulls[Natts_pg_language]; + NameData langname; + HeapTuple tup; ++ Oid langSecid; + ObjectAddress myself, + referenced; + + /* ++ * SELinux permission checks ++ */ ++ langSecid = sepgsql_language_create(languageName, handlerOid, valOid); ++ ++ /* + * Insert the new language into pg_language + */ + rel = heap_open(LanguageRelationId, RowExclusiveLock); +@@ -297,6 +306,8 @@ create_proc_lang(const char *languageName, + nulls[Anum_pg_language_lanacl - 1] = true; + + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, langSecid); + + simple_heap_insert(rel, tup); + +@@ -518,6 +529,9 @@ RenameLanguage(const char *oldname, const char *newname) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, + oldname); + ++ /* SELinux permission checks */ ++ sepgsql_language_alter(HeapTupleGetOid(tup)); ++ + /* rename */ + namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -613,6 +627,9 @@ AlterLanguageOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), newOwnerId); + ++ /* SELinux permission checks */ ++ sepgsql_language_alter(HeapTupleGetOid(tup)); ++ + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + +diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c +index 0d047cf..748bdd6 100644 +--- a/src/backend/commands/schemacmds.c ++++ b/src/backend/commands/schemacmds.c +@@ -25,6 +25,7 @@ + #include "commands/schemacmds.h" + #include "miscadmin.h" + #include "parser/parse_utilcmd.h" ++#include "security/sepgsql.h" + #include "tcop/utility.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -48,6 +49,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) + ListCell *parsetree_item; + Oid owner_uid; + Oid saved_uid; ++ Oid nspsecid; + int save_sec_context; + AclResult aclresult; + +@@ -75,6 +77,10 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) + + check_is_member_of_role(saved_uid, owner_uid); + ++ /* SELinux checks db_schema:{create} */ ++ nspsecid = sepgsql_schema_create(schemaName, false, ++ (DefElem *)stmt->secLabel); ++ + /* Additional check to protect reserved schema names */ + if (!allowSystemTableMods && IsReservedName(schemaName)) + ereport(ERROR, +@@ -95,7 +101,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); + + /* Create the schema's namespace */ +- namespaceId = NamespaceCreate(schemaName, owner_uid); ++ namespaceId = NamespaceCreate(schemaName, owner_uid, nspsecid); + + /* Advance cmd counter to make the namespace visible */ + CommandCounterIncrement(); +@@ -268,8 +274,7 @@ RenameSchema(const char *oldname, const char *newname) + errmsg("schema \"%s\" does not exist", oldname))); + + /* make sure the new name doesn't exist */ +- if (HeapTupleIsValid( +- SearchSysCache(NAMESPACENAME, ++ if (HeapTupleIsValid(SearchSysCache(NAMESPACENAME, + CStringGetDatum(newname), + 0, 0, 0))) + ereport(ERROR, +@@ -287,6 +292,9 @@ RenameSchema(const char *oldname, const char *newname) + aclcheck_error(aclresult, ACL_KIND_DATABASE, + get_database_name(MyDatabaseId)); + ++ /* SELinux checks db_schema:{setattr} */ ++ sepgsql_schema_alter(HeapTupleGetOid(tup)); ++ + if (!allowSystemTableMods && IsReservedName(newname)) + ereport(ERROR, + (errcode(ERRCODE_RESERVED_NAME), +@@ -398,6 +406,9 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_DATABASE, + get_database_name(MyDatabaseId)); + ++ /* SELinux checks db_schema:{setattr} */ ++ sepgsql_schema_alter(HeapTupleGetOid(tup)); ++ + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + +@@ -432,3 +443,51 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) + } + + } ++ ++/* ++ * ALTER SCHEMA name SECURITY_LABEL [=] newlabel ++ */ ++void ++AlterSchemaSecLabel(const char *name, DefElem *secLabel) ++{ ++ Relation rel; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ Oid secid; ++ bool replaces[Natts_pg_namespace]; ++ ++ /* open pg_namespace relation */ ++ rel = heap_open(NamespaceRelationId, RowExclusiveLock); ++ oldtup = SearchSysCache(NAMESPACENAME, ++ CStringGetDatum(name), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(oldtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_SCHEMA), ++ errmsg("schema \"%s\" does not exist", name))); ++ ++ memset(replaces, false, sizeof(replaces)); ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ NULL, NULL, replaces); ++ if (!HeapTupleHasSecid(newtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set security label on \"%s\"", name))); ++ ++ ReleaseSysCache(oldtup); ++ ++ /* DAC permission check */ ++ if (!pg_namespace_ownercheck(HeapTupleGetOid(newtup), GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, name); ++ /* SELinux checks db_schema:{setattr relabelfrom relabelto} */ ++ secid = sepgsql_schema_relabel(HeapTupleGetOid(newtup), secLabel); ++ HeapTupleSetSecid(newtup, secid); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ ++ heap_close(rel, RowExclusiveLock); ++} +diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c +index abc65aa..9507ef1 100644 +--- a/src/backend/commands/sequence.c ++++ b/src/backend/commands/sequence.c +@@ -26,6 +26,7 @@ + #include "commands/tablecmds.h" + #include "miscadmin.h" + #include "nodes/makefuncs.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/proc.h" +@@ -201,6 +202,7 @@ DefineSequence(CreateSeqStmt *seq) + stmt->options = list_make1(defWithOids(false)); + stmt->oncommit = ONCOMMIT_NOOP; + stmt->tablespacename = NULL; ++ stmt->secLabel = seq->secLabel; + + seqoid = DefineRelation(stmt, RELKIND_SEQUENCE); + +@@ -328,6 +330,8 @@ AlterSequence(AlterSeqStmt *stmt) + if (!pg_class_ownercheck(relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + stmt->sequence->relname); ++ /* SELinux checks db_sequence:{setattr} */ ++ sepgsql_relation_alter(relid, NULL, InvalidOid); + + /* do the work */ + AlterSequenceInternal(relid, stmt->options); +@@ -467,6 +471,9 @@ nextval_internal(Oid relid) + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux check db_sequence:{next_value} */ ++ sepgsql_sequence_next_value(elm->relid); ++ + if (elm->last != elm->cached) /* some numbers were cached */ + { + Assert(elm->last_valid); +@@ -662,6 +669,9 @@ currval_oid(PG_FUNCTION_ARGS) + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux check db_sequence:{get_value} */ ++ sepgsql_sequence_get_value(elm->relid); ++ + if (!elm->last_valid) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), +@@ -706,6 +716,9 @@ lastval(PG_FUNCTION_ARGS) + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux check db_sequence:{get_value} */ ++ sepgsql_sequence_get_value(last_used_seq->relid); ++ + result = last_used_seq->last; + relation_close(seqrel, NoLock); + +@@ -742,6 +755,9 @@ do_setval(Oid relid, int64 next, bool iscalled) + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux check db_sequence:{set_value} */ ++ sepgsql_sequence_set_value(elm->relid); ++ + /* lock page' buffer and read tuple */ + seq = read_info(elm, seqrel, &buf); + +diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c +index 96dda00..85b8800 100644 +--- a/src/backend/commands/tablecmds.c ++++ b/src/backend/commands/tablecmds.c +@@ -62,6 +62,7 @@ + #include "parser/parser.h" + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteHandler.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/smgr.h" +@@ -260,8 +261,8 @@ static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, + static void ATRewriteTables(List **wqueue); + static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap); + static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel); +-static void ATSimplePermissions(Relation rel, bool allowView); +-static void ATSimplePermissionsRelationOrIndex(Relation rel); ++static void ATSimplePermissions(Relation rel, const char *colname, bool allowView); ++static void ATSimplePermissionsRelationOrIndex(Relation rel, const char *colname); + static void ATSimpleRecursion(List **wqueue, Relation rel, + AlterTableCmd *cmd, bool recurse); + static void ATOneLevelRecursion(List **wqueue, Relation rel, +@@ -351,6 +352,7 @@ DefineRelation(CreateStmt *stmt, char relkind) + List *rawDefaults; + List *cookedDefaults; + Datum reloptions; ++ Oid *secLabels; + ListCell *listptr; + AttrNumber attnum; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; +@@ -454,6 +456,16 @@ DefineRelation(CreateStmt *stmt, char relkind) + localHasOids = interpretOidsOption(stmt->options); + descriptor->tdhasoid = (localHasOids || parentOidCount > 0); + ++ /* SELinux checks db_table:{create} and db_column:{create} */ ++ secLabels = sepgsql_relation_create(relname, ++ relkind, ++ descriptor, ++ namespaceId, ++ (DefElem *)stmt->secLabel, ++ schema, ++ false, ++ true); ++ + /* + * Find columns with default values and prepare for insertion of the + * defaults. Pre-cooked (that is, inherited) defaults go into a list of +@@ -523,7 +535,8 @@ DefineRelation(CreateStmt *stmt, char relkind) + parentOidCount, + stmt->oncommit, + reloptions, +- allowSystemTableMods); ++ allowSystemTableMods, ++ secLabels); + + StoreCatalogInheritance(relationId, inheritOids); + +@@ -897,6 +910,8 @@ ExecuteTruncate(TruncateStmt *stmt) + if (!pg_class_ownercheck(seq_relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(seq_rel)); ++ /* SELinux checks */ ++ sepgsql_relation_alter(seq_relid, NULL, InvalidOid); + + seq_relids = lappend_oid(seq_relids, seq_relid); + +@@ -1052,6 +1067,9 @@ truncate_check_rel(Relation rel) + errmsg("permission denied: \"%s\" is a system catalog", + RelationGetRelationName(rel)))); + ++ /* SELinux: check db_table:{delete} permission */ ++ sepgsql_relation_truncate(rel); ++ + /* + * We can never allow truncation of shared or nailed-in-cache relations, + * because we can't support changing their relfilenode values. +@@ -1226,6 +1244,8 @@ MergeAttributes(List *schema, List *supers, bool istemp, + if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(relation)); ++ /* SELinux checks db_table:{setattr} */ ++ sepgsql_relation_alter(RelationGetRelid(relation), NULL, InvalidOid); + + /* + * Reject duplications in the list of parents. +@@ -1931,6 +1951,9 @@ renameatt(Oid myrelid, + errmsg("cannot rename system column \"%s\"", + oldattname))); + ++ /* SELinux checks db_column:{setattr} */ ++ sepgsql_attribute_alter(myrelid, oldattname); ++ + /* + * if the attribute is inherited, forbid the renaming, unless we are + * already inside a recursive rename. +@@ -2036,6 +2059,9 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype) + Oid namespaceId; + char relkind; + ++ /* SELinux checks */ ++ sepgsql_relation_alter(myrelid, newrelname, InvalidOid); ++ + /* + * Grab an exclusive lock on the target table, index, sequence or view, + * which we will NOT release until end of transaction. +@@ -2369,14 +2395,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, + switch (cmd->subtype) + { + case AT_AddColumn: /* ADD COLUMN */ +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, NULL, false); + /* Performs own recursion */ + ATPrepAddColumn(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; + break; + case AT_AddColumnToView: /* add column via CREATE OR REPLACE + * VIEW */ +- ATSimplePermissions(rel, true); ++ ATSimplePermissions(rel, NULL, true); + /* Performs own recursion */ + ATPrepAddColumn(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; +@@ -2389,19 +2415,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, + * substitutes default values into INSERTs before it expands + * rules. + */ +- ATSimplePermissions(rel, true); ++ ATSimplePermissions(rel, cmd->name, true); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP; + break; + case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, cmd->name, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_DROP; + break; + case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, cmd->name, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_ADD_CONSTR; +@@ -2413,13 +2439,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, + pass = AT_PASS_COL_ATTRS; + break; + case AT_SetStorage: /* ALTER COLUMN STORAGE */ +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, cmd->name, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_COL_ATTRS; + break; + case AT_DropColumn: /* DROP COLUMN */ +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, NULL, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +@@ -2427,13 +2453,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, + pass = AT_PASS_DROP; + break; + case AT_AddIndex: /* ADD INDEX */ +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, NULL, false); + /* This command never recurses */ + /* No command-specific prep needed */ + pass = AT_PASS_ADD_INDEX; + break; + case AT_AddConstraint: /* ADD CONSTRAINT */ +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, NULL, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +@@ -2441,7 +2467,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, + pass = AT_PASS_ADD_CONSTR; + break; + case AT_DropConstraint: /* DROP CONSTRAINT */ +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, NULL, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +@@ -2449,7 +2475,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, + pass = AT_PASS_DROP; + break; + case AT_AlterColumnType: /* ALTER COLUMN TYPE */ +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, cmd->name, false); + /* Performs own recursion */ + ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd); + pass = AT_PASS_ALTER_TYPE; +@@ -2461,20 +2487,20 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, + break; + case AT_ClusterOn: /* CLUSTER ON */ + case AT_DropCluster: /* SET WITHOUT CLUSTER */ +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, NULL, false); + /* These commands never recurse */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; + break; + case AT_AddOids: /* SET WITH OIDS */ +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, NULL, false); + /* Performs own recursion */ + if (!rel->rd_rel->relhasoids || recursing) + ATPrepAddOids(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; + break; + case AT_DropOids: /* SET WITHOUT OIDS */ +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, NULL, false); + /* Performs own recursion */ + if (rel->rd_rel->relhasoids) + { +@@ -2488,14 +2514,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, + pass = AT_PASS_DROP; + break; + case AT_SetTableSpace: /* SET TABLESPACE */ +- ATSimplePermissionsRelationOrIndex(rel); ++ ATSimplePermissionsRelationOrIndex(rel, NULL); + /* This command never recurses */ + ATPrepSetTableSpace(tab, rel, cmd->name); + pass = AT_PASS_MISC; /* doesn't actually matter */ + break; + case AT_SetRelOptions: /* SET (...) */ + case AT_ResetRelOptions: /* RESET (...) */ +- ATSimplePermissionsRelationOrIndex(rel); ++ ATSimplePermissionsRelationOrIndex(rel, NULL); + /* This command never recurses */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; +@@ -2514,7 +2540,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, + case AT_DisableRule: + case AT_AddInherit: /* INHERIT / NO INHERIT */ + case AT_DropInherit: +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, NULL, false); + /* These commands never recurse */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; +@@ -2860,8 +2886,9 @@ ATRewriteTables(List **wqueue) + /* + * The new relation is local to our transaction and we know + * nothing depends on it, so DROP_RESTRICT should be OK. ++ * SELinux does not apply any permission checks here. + */ +- performDeletion(&object, DROP_RESTRICT); ++ performDeletionNoPerms(&object, DROP_RESTRICT); + /* performDeletion does CommandCounterIncrement at end */ + + /* +@@ -3086,11 +3113,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) + if (newrel) + { + Oid tupOid = InvalidOid; ++ Oid tupSecid = InvalidOid; + + /* Extract data from old tuple */ + heap_deform_tuple(tuple, oldTupDesc, values, isnull); + if (oldTupDesc->tdhasoid) + tupOid = HeapTupleGetOid(tuple); ++ if (HeapTupleHasSecid(tuple)) ++ tupSecid = HeapTupleGetSecid(tuple); + + /* Set dropped attributes to null in new tuple */ + foreach(lc, dropped_attrs) +@@ -3122,6 +3152,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) + /* Preserve OID, if any */ + if (newTupDesc->tdhasoid) + HeapTupleSetOid(tuple, tupOid); ++ /* Preserve SID, if any */ ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, tupSecid); + } + + /* Now check any constraints on the possibly-changed tuple */ +@@ -3223,7 +3256,7 @@ ATGetQueueEntry(List **wqueue, Relation rel) + * - Ensure that it is not a system table + */ + static void +-ATSimplePermissions(Relation rel, bool allowView) ++ATSimplePermissions(Relation rel, const char *colName, bool allowView) + { + if (rel->rd_rel->relkind != RELKIND_RELATION) + { +@@ -3247,6 +3280,12 @@ ATSimplePermissions(Relation rel, bool allowView) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + ++ /* SELinux checks */ ++ if (!colName) ++ sepgsql_relation_alter(RelationGetRelid(rel), NULL, InvalidOid); ++ else ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + if (!allowSystemTableMods && IsSystemRelation(rel)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), +@@ -3262,7 +3301,7 @@ ATSimplePermissions(Relation rel, bool allowView) + * - Ensure that it is not a system table + */ + static void +-ATSimplePermissionsRelationOrIndex(Relation rel) ++ATSimplePermissionsRelationOrIndex(Relation rel, const char *colName) + { + if (rel->rd_rel->relkind != RELKIND_RELATION && + rel->rd_rel->relkind != RELKIND_INDEX) +@@ -3276,6 +3315,12 @@ ATSimplePermissionsRelationOrIndex(Relation rel) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + ++ /* SELinux checks */ ++ if (!colName) ++ sepgsql_relation_alter(RelationGetRelid(rel), NULL, InvalidOid); ++ else ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + if (!allowSystemTableMods && IsSystemRelation(rel)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), +@@ -3519,6 +3564,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + HeapTuple typeTuple; + Oid typeOid; + int32 typmod; ++ Oid attsecid; + Form_pg_type tform; + Expr *defval; + +@@ -3556,6 +3602,9 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + errmsg("child table \"%s\" has a conflicting \"%s\" column", + RelationGetRelationName(rel), colDef->colname))); + ++ /* SELinux checks db_column:{setattr} */ ++ sepgsql_attribute_alter(myrelid, colDef->colname); ++ + /* Bump the existing child att's inhcount */ + childatt->attinhcount++; + simple_heap_update(attrdesc, &tuple->t_self, tuple); +@@ -3595,6 +3644,9 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + errmsg("column \"%s\" of relation \"%s\" already exists", + colDef->colname, RelationGetRelationName(rel)))); + ++ /* SELinux checks db_column:{create} */ ++ attsecid = sepgsql_attribute_create(myrelid, colDef); ++ + /* Determine the new attribute's number */ + if (isOid) + newattnum = ObjectIdAttributeNumber; +@@ -3637,7 +3689,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + + ReleaseSysCache(typeTuple); + +- InsertPgAttributeTuple(attrdesc, &attribute, NULL); ++ InsertPgAttributeTuple(attrdesc, &attribute, NULL, attsecid); + + heap_close(attrdesc, RowExclusiveLock); + +@@ -4026,6 +4078,8 @@ ATPrepSetStatistics(Relation rel, const char *colName, Node *flagValue) + if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); + } + + static void +@@ -4181,7 +4235,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, NULL, false); + + /* + * get the number of the attribute +@@ -4483,7 +4537,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, NULL, false); + + /* + * Call AddRelationNewConstraints to do the work, making sure it works on +@@ -5385,7 +5439,7 @@ ATExecDropConstraint(Relation rel, const char *constrName, + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +- ATSimplePermissions(rel, false); ++ ATSimplePermissions(rel, NULL, false); + + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + +@@ -6319,6 +6373,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks db_table:{setattr} */ ++ sepgsql_relation_alter(relationOid, NULL, InvalidOid); + } + + memset(repl_null, false, sizeof(repl_null)); +@@ -6923,7 +6979,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent) + * Must be owner of both parent and child -- child was checked by + * ATSimplePermissions call in ATPrepCmd + */ +- ATSimplePermissions(parent_rel, false); ++ ATSimplePermissions(parent_rel, NULL, false); + + /* Permanent rels cannot inherit from temporary ones */ + if (parent_rel->rd_istemp && !child_rel->rd_istemp) +@@ -7581,6 +7637,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, + RelationGetRelationName(rel), + newschema))); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(relid, NULL, nspOid); ++ + /* disallow renaming into or out of temp schemas */ + if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid)) + ereport(ERROR, +@@ -7773,6 +7832,134 @@ AlterSeqNamespaces(Relation classRel, Relation rel, + relation_close(depRel, AccessShareLock); + } + ++/* ++ * ALTER TABLE/SEQUENCE name SECURITY_LABEL [=] newlabel ++ * ALTER TABLE/SEQUENCE name ALTER column SECURITY_LABEL [=] newlabel ++ */ ++static void ++ExecRelationSetSecLabel(Oid relid, DefElem *seclabel) ++{ ++ Relation rel; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ Oid secid; ++ bool replaces[Natts_pg_class]; ++ ++ rel = heap_open(RelationRelationId, RowExclusiveLock); ++ oldtup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(oldtup)) ++ elog(ERROR, "cache lookup failed for relation: %u", relid); ++ ++ memset(replaces, false, sizeof(replaces)); ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ NULL, NULL, replaces); ++ if (!HeapTupleHasSecid(newtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set security label on \"%s\"", ++ get_rel_name(relid)))); ++ ++ ReleaseSysCache(oldtup); ++ ++ /* SELinux checks db_table:{setattr relabelfrom relabelto} */ ++ secid = sepgsql_relation_relabel(relid, seclabel); ++ ++ HeapTupleSetSecid(newtup, secid); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++static void ++ExecAttributeSetSecLabel(Oid relid, const char *attname, DefElem *seclabel) ++{ ++ Relation rel; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ AttrNumber attnum; ++ Oid secid; ++ bool replaces[Natts_pg_attribute]; ++ ++ rel = heap_open(AttributeRelationId, RowExclusiveLock); ++ oldtup = SearchSysCacheAttName(relid, attname); ++ if (!HeapTupleIsValid(oldtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_COLUMN), ++ errmsg("column \"%s\" of relation \"%s\" does not exist", ++ attname, get_rel_name(relid)))); ++ attnum = ((Form_pg_attribute) GETSTRUCT(oldtup))->attnum; ++ ++ memset(replaces, false, sizeof(replaces)); ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ NULL, NULL, replaces); ++ if (!HeapTupleHasSecid(newtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set security context on \"%s.%s\"", ++ get_rel_name(relid), attname))); ++ ++ ReleaseSysCache(oldtup); ++ ++ /* SELinux checks db_column:{setattr relabelfrom relabelto} */ ++ secid = sepgsql_attribute_relabel(relid, attnum, seclabel); ++ ++ HeapTupleSetSecid(newtup, secid); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++void ++AlterRelationSecLabel(RangeVar *relation, const char *attname, ++ ObjectType objtype, DefElem *seclabel) ++{ ++ Oid relid; ++ char relkind; ++ ++ /* Check relation type against type specified in the ALTER command */ ++ relid = RangeVarGetRelid(relation, false); ++ relkind = get_rel_relkind(relid); ++ ++ switch (objtype) ++ { ++ case OBJECT_TABLE: ++ case OBJECT_COLUMN: ++ if (relkind != RELKIND_RELATION) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a table", get_rel_name(relid)))); ++ break; ++ ++ case OBJECT_SEQUENCE: ++ if (relkind != RELKIND_SEQUENCE) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a sequence", get_rel_name(relid)))); ++ break; ++ ++ default: ++ elog(ERROR, "unrecognized object type: %d", (int)objtype); ++ break; ++ } ++ ++ /* Exec set security label */ ++ if (objtype != OBJECT_COLUMN) ++ ExecRelationSetSecLabel(relid, seclabel); ++ else ++ ExecAttributeSetSecLabel(relid, attname, seclabel); ++} + + /* + * This code supports +diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c +index f432e74..469b5cb 100644 +--- a/src/backend/commands/trigger.c ++++ b/src/backend/commands/trigger.c +@@ -33,6 +33,7 @@ + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" + #include "pgstat.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "tcop/utility.h" + #include "utils/acl.h" +@@ -182,6 +183,10 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid, bool checkPermissions) + NameListToString(stmt->funcname)))); + } + ++ /* SELinux checks */ ++ if (checkPermissions) ++ sepgsql_trigger_create(RelationGetRelid(rel), stmt->trigname, funcoid); ++ + /* + * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that + * references one of the built-in RI_FKey trigger functions, assume it is +@@ -746,6 +751,7 @@ DropTrigger(Oid relid, const char *trigname, DropBehavior behavior, + if (!pg_class_ownercheck(relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(relid)); ++ sepgsql_trigger_drop(relid, trigname); + + object.classId = TriggerRelationId; + object.objectId = HeapTupleGetOid(tup); +@@ -862,6 +868,9 @@ renametrig(Oid relid, + */ + targetrel = heap_open(relid, AccessExclusiveLock); + ++ /* SELinux checks */ ++ sepgsql_trigger_alter(relid, oldname); ++ + /* + * Scan pg_trigger twice for existing triggers on relation. We do this in + * order to ensure a trigger does not exist with newname (The unique index +diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c +index a1f301b..16cfa5d 100644 +--- a/src/backend/commands/tsearchcmds.c ++++ b/src/backend/commands/tsearchcmds.c +@@ -35,6 +35,7 @@ + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" ++#include "security/sepgsql.h" + #include "tsearch/ts_cache.h" + #include "tsearch/ts_public.h" + #include "tsearch/ts_utils.h" +@@ -171,6 +172,7 @@ DefineTSParser(List *names, List *parameters) + NameData pname; + Oid prsOid; + Oid namespaceoid; ++ Oid secid; + + if (!superuser()) + ereport(ERROR, +@@ -250,12 +252,22 @@ DefineTSParser(List *names, List *parameters) + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("text search parser lextypes method is required"))); + ++ /* Permission checks */ ++ secid = sepgsql_ts_parser_create(prsname, namespaceoid, ++ DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prsheadline - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])); ++ + /* + * Looks good, insert + */ + prsRel = heap_open(TSParserRelationId, RowExclusiveLock); + + tup = heap_form_tuple(prsRel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + prsOid = simple_heap_insert(prsRel, tup); + +@@ -372,6 +384,9 @@ RenameTSParser(List *oldname, const char *newname) + + prsId = TSParserGetPrsid(oldname, false); + ++ /* SELinux checks */ ++ sepgsql_ts_parser_alter(prsId, newname); ++ + tup = SearchSysCacheCopy(TSPARSEROID, + ObjectIdGetDatum(prsId), + 0, 0, 0); +@@ -503,6 +518,7 @@ DefineTSDictionary(List *names, List *parameters) + List *dictoptions = NIL; + Oid dictOid; + Oid namespaceoid; ++ Oid secid; + AclResult aclresult; + char *dictname; + +@@ -515,6 +531,9 @@ DefineTSDictionary(List *names, List *parameters) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceoid)); + ++ /* SELinux check */ ++ secid = sepgsql_ts_dict_create(dictname, namespaceoid); ++ + /* + * loop over the definition list and extract the information we need. + */ +@@ -563,6 +582,8 @@ DefineTSDictionary(List *names, List *parameters) + dictRel = heap_open(TSDictionaryRelationId, RowExclusiveLock); + + tup = heap_form_tuple(dictRel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + dictOid = simple_heap_insert(dictRel, tup); + +@@ -621,6 +642,9 @@ RenameTSDictionary(List *oldname, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId, newname); ++ + namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); +@@ -762,6 +786,9 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, + NameListToString(stmt->dictname)); + ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId, NULL); ++ + /* deserialize the existing set of options */ + opt = SysCacheGetAttr(TSDICTOID, tup, + Anum_pg_ts_dict_dictinitoption, +@@ -889,6 +916,8 @@ AlterTSDictionaryOwner(List *name, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId, NULL); + + form->dictowner = newOwnerId; + +@@ -999,6 +1028,7 @@ DefineTSTemplate(List *names, List *parameters) + NameData dname; + int i; + Oid dictOid; ++ Oid dictSecid; + Oid namespaceoid; + char *tmplname; + +@@ -1054,6 +1084,11 @@ DefineTSTemplate(List *names, List *parameters) + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("text search template lexize method is required"))); + ++ /* SELinux checks */ ++ dictSecid = sepgsql_ts_template_create(tmplname, namespaceoid, ++ DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])); ++ + /* + * Looks good, insert + */ +@@ -1061,6 +1096,8 @@ DefineTSTemplate(List *names, List *parameters) + tmplRel = heap_open(TSTemplateRelationId, RowExclusiveLock); + + tup = heap_form_tuple(tmplRel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, dictSecid); + + dictOid = simple_heap_insert(tmplRel, tup); + +@@ -1093,6 +1130,9 @@ RenameTSTemplate(List *oldname, const char *newname) + + tmplId = TSTemplateGetTmplid(oldname, false); + ++ /* Permission checks */ ++ sepgsql_ts_template_alter(tmplId, newname); ++ + tup = SearchSysCacheCopy(TSTEMPLATEOID, + ObjectIdGetDatum(tmplId), + 0, 0, 0); +@@ -1335,6 +1375,7 @@ DefineTSConfiguration(List *names, List *parameters) + Oid sourceOid = InvalidOid; + Oid prsOid = InvalidOid; + Oid cfgOid; ++ Oid cfgSecid; + ListCell *pl; + + /* Convert list of names to a name and namespace */ +@@ -1399,6 +1440,9 @@ DefineTSConfiguration(List *names, List *parameters) + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("text search parser is required"))); + ++ /* SELinux checks */ ++ cfgSecid = sepgsql_ts_config_create(cfgname, namespaceoid); ++ + /* + * Looks good, build tuple and insert + */ +@@ -1414,6 +1458,8 @@ DefineTSConfiguration(List *names, List *parameters) + cfgRel = heap_open(TSConfigRelationId, RowExclusiveLock); + + tup = heap_form_tuple(cfgRel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, cfgSecid); + + cfgOid = simple_heap_insert(cfgRel, tup); + +@@ -1519,6 +1565,9 @@ RenameTSConfiguration(List *oldname, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* permission checks */ ++ sepgsql_ts_config_alter(cfgId, newname); ++ + namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); +@@ -1690,6 +1739,8 @@ AlterTSConfigurationOwner(List *name, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_ts_config_alter(cfgId, NULL); + + form->cfgowner = newOwnerId; + +@@ -1727,6 +1778,9 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, + NameListToString(stmt->cfgname)); + ++ /* SELinux checks */ ++ sepgsql_ts_config_alter(HeapTupleGetOid(tup), NULL); ++ + relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock); + + /* Add or drop mappings */ +diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c +index 528a917..e10b914 100644 +--- a/src/backend/commands/typecmds.c ++++ b/src/backend/commands/typecmds.c +@@ -56,6 +56,7 @@ + #include "parser/parse_expr.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -1543,6 +1544,7 @@ AlterDomainDefault(List *names, Node *defaultRaw) + + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); ++ sepgsql_type_alter(domainoid, NULL, InvalidOid); + + /* Setup new tuple */ + MemSet(new_record, (Datum) 0, sizeof(new_record)); +@@ -1671,6 +1673,7 @@ AlterDomainNotNull(List *names, bool notNull) + + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); ++ sepgsql_type_alter(domainoid, NULL, InvalidOid); + + /* Is the domain already set to the desired constraint? */ + if (typTup->typnotnull == notNull) +@@ -1772,6 +1775,7 @@ AlterDomainDropConstraint(List *names, const char *constrName, + + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); ++ sepgsql_type_alter(domainoid, NULL, InvalidOid); + + /* Grab an appropriate lock on the pg_constraint relation */ + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); +@@ -1848,6 +1852,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) + + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); ++ sepgsql_type_alter(domainoid, NULL, InvalidOid); + + /* Check for unsupported constraint types */ + if (IsA(newConstraint, FkConstraint)) +@@ -2470,6 +2475,9 @@ RenameType(List *names, const char *newTypeName) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, + format_type_be(typeOid)); + ++ /* SELinux check permission */ ++ sepgsql_type_alter(typeOid, newTypeName, InvalidOid); ++ + /* + * If it's a composite type, we need to check that it really is a + * free-standing composite type, and not a table's rowtype. We want people +@@ -2590,6 +2598,8 @@ AlterTypeOwner(List *names, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(typTup->typnamespace)); + } ++ /* SELinux checks permissions */ ++ sepgsql_type_alter(HeapTupleGetOid(tup), NULL, InvalidOid); + + /* + * If it's a composite type, invoke ATExecChangeOwner so that we fix +@@ -2706,6 +2716,9 @@ AlterTypeNamespace(List *names, const char *newschema) + errhint("You can alter type %s, which will alter the array type as well.", + format_type_be(elemOid)))); + ++ /* SELinux checks permissions */ ++ sepgsql_type_alter(typeOid, NULL, nspOid); ++ + /* and do the work */ + AlterTypeNamespaceInternal(typeOid, nspOid, false, true); + } +diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c +index dcb30f8..737b58a 100644 +--- a/src/backend/commands/vacuum.c ++++ b/src/backend/commands/vacuum.c +@@ -32,6 +32,7 @@ + #include "catalog/namespace.h" + #include "catalog/pg_database.h" + #include "catalog/pg_namespace.h" ++#include "catalog/pg_security.h" + #include "catalog/storage.h" + #include "commands/dbcommands.h" + #include "commands/vacuum.h" +@@ -1209,6 +1210,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound, + /* all done with this class, but hold lock until commit */ + relation_close(onerel, NoLock); + ++ /* Also reclaim orphan security label */ ++ seclabelRelationReclaim(relid); ++ + /* + * Complete the transaction and free all temporary memory used. + */ +diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c +index dfbce72..1bfab03 100644 +--- a/src/backend/commands/view.c ++++ b/src/backend/commands/view.c +@@ -28,6 +28,7 @@ + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteManip.h" + #include "rewrite/rewriteSupport.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -166,6 +167,9 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + ++ /* SELinux checks */ ++ sepgsql_view_replace(viewOid); ++ + /* Also check it's not in use already */ + CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW"); + +diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c +index 564347f..154b3c4 100644 +--- a/src/backend/executor/execJunk.c ++++ b/src/backend/executor/execJunk.c +@@ -60,7 +60,8 @@ + * An optional resultSlot can be passed as well. + */ + JunkFilter * +-ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) ++ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel, ++ TupleTableSlot *slot) + { + JunkFilter *junkfilter; + TupleDesc cleanTupType; +@@ -72,7 +73,7 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) + /* + * Compute the tuple descriptor for the cleaned tuple. + */ +- cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); ++ cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hasseclabel); + + /* + * Use the given slot, or make a new slot if we weren't given one. +diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c +index 131be22..5c4205b 100644 +--- a/src/backend/executor/execMain.c ++++ b/src/backend/executor/execMain.c +@@ -39,6 +39,7 @@ + #include "access/xact.h" + #include "catalog/heap.h" + #include "catalog/namespace.h" ++#include "catalog/pg_security.h" + #include "catalog/toasting.h" + #include "commands/tablespace.h" + #include "commands/trigger.h" +@@ -50,6 +51,7 @@ + #include "optimizer/clauses.h" + #include "parser/parse_clause.h" + #include "parser/parsetree.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/smgr.h" +@@ -442,7 +444,10 @@ ExecCheckRTPerms(List *rangeTable) + + foreach(l, rangeTable) + { +- ExecCheckRTEPerms((RangeTblEntry *) lfirst(l)); ++ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); ++ ++ ExecCheckRTEPerms(rte); ++ sepgsqlCheckRTEPerms(rte); + } + } + +@@ -901,16 +906,16 @@ InitPlan(QueryDesc *queryDesc, int eflags) + for (i = 0; i < as_nplans; i++) + { + PlanState *subplan = appendplans[i]; ++ Relation resultRel = resultRelInfo->ri_RelationDesc; + JunkFilter *j; + + if (operation == CMD_UPDATE) +- ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, +- subplan->plan->targetlist); ++ ExecCheckPlanOutput(resultRel, subplan->plan->targetlist); + + j = ExecInitJunkFilter(subplan->plan->targetlist, +- resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, +- ExecAllocTableSlot(estate->es_tupleTable)); +- ++ RelationGetDescr(resultRel)->tdhasoid, ++ RelationGetDescr(resultRel)->tdhassecid, ++ ExecAllocTableSlot(estate->es_tupleTable)); + /* + * Since it must be UPDATE/DELETE, there had better be a + * "ctid" junk attribute in the tlist ... but ctid could +@@ -953,6 +958,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) + + j = ExecInitJunkFilter(planstate->plan->targetlist, + tupType->tdhasoid, ++ tupType->tdhassecid, + ExecAllocTableSlot(estate->es_tupleTable)); + estate->es_junkFilter = j; + if (estate->es_result_relation_info) +@@ -1023,7 +1029,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) + * We assume all the sublists will generate the same output tupdesc. + */ + tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists), +- false); ++ false, false); + + /* Set up a slot for the output of the RETURNING projection(s) */ + slot = ExecAllocTableSlot(estate->es_tupleTable); +@@ -1346,6 +1352,37 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids) + return false; + } + ++/* ++ * ExecContextForcesSecids ++ * ++ * We need to ensure that result tuples have space for security identifier. ++ * if the security feature need to store it within the given relation. ++ */ ++bool ExecContextForcesSecids(PlanState *planstate, bool *hassecid) ++{ ++ if (planstate->state->es_select_into) ++ { ++ *hassecid = securityTupleDescHasSecid(InvalidOid, ++ RELKIND_RELATION); ++ return true; ++ } ++ else ++ { ++ ResultRelInfo *ri = planstate->state->es_result_relation_info; ++ ++ if (ri && ri->ri_RelationDesc) ++ { ++ Oid relid = RelationGetRelid(ri->ri_RelationDesc); ++ char relkind = RelationGetForm(ri->ri_RelationDesc)->relkind; ++ ++ *hassecid = securityTupleDescHasSecid(relid, relkind); ++ ++ return true; ++ } ++ } ++ return false; ++} ++ + /* ---------------------------------------------------------------- + * ExecEndPlan + * +@@ -1426,6 +1463,58 @@ ExecEndPlan(PlanState *planstate, EState *estate) + } + } + ++/* ++ * fetchWritableSystemAttribute() fetches writable system column data ++ * using Junkfilter, and saves them at TupleTableSlot temporary. ++ * ++ * storeWritableSystemAttribute() copies these fetched data into ++ * header structure of HeapTuple. ++ */ ++static void ++fetchWritableSystemAttribute(JunkFilter *junkfilter, TupleTableSlot *slot, ++ Datum *tts_seclabel) ++{ ++ AttrNumber attno; ++ Datum datum; ++ bool isnull; ++ ++ /* for Security Label */ ++ attno = ExecFindJunkAttribute(junkfilter, SecurityAttributeName); ++ if (attno != InvalidAttrNumber) ++ { ++ datum = ExecGetJunkAttribute(slot, attno, &isnull); ++ if (isnull) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set NULL on \"%s\"", ++ SecurityAttributeName))); ++ *tts_seclabel = datum; ++ } ++} ++ ++static void ++storeWritableSystemAttribute(Relation rel, TupleTableSlot *slot, HeapTuple tuple) ++{ ++ Oid relid = RelationGetRelid(rel); ++ Oid secid; ++ ++ /* "security_label" */ ++ if (DatumGetPointer(slot->tts_seclabel) != NULL) ++ { ++ char *seclabel = TextDatumGetCString(slot->tts_seclabel); ++ ++ if (!HeapTupleHasSecid(tuple)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to assign security label on \"%s\"", ++ RelationGetRelationName(rel)))); ++ secid = securityTransSecLabelIn(relid, seclabel); ++ HeapTupleSetSecid(tuple, secid); ++ } ++ else if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, InvalidOid); ++} ++ + /* ---------------------------------------------------------------- + * ExecutePlan + * +@@ -1487,6 +1576,8 @@ ExecutePlan(EState *estate, + */ + for (;;) + { ++ Datum tts_seclabel = PointerGetDatum(NULL); ++ + /* Reset the per-output-tuple exprcontext */ + ResetPerTupleExprContext(estate); + +@@ -1631,6 +1722,11 @@ lnext: ; + } + + /* ++ * extract writable system attribute ++ */ ++ fetchWritableSystemAttribute(junkfilter, slot, &tts_seclabel); ++ ++ /* + * extract the 'ctid' junk attribute. + */ + if (operation == CMD_UPDATE || operation == CMD_DELETE) +@@ -1657,6 +1753,7 @@ lnext: ; + if (operation != CMD_DELETE) + slot = ExecFilterJunk(junkfilter, slot); + } ++ slot->tts_seclabel = tts_seclabel; + + /* + * now that we have a tuple, do the appropriate thing with it.. either +@@ -1781,6 +1878,8 @@ ExecInsert(TupleTableSlot *slot, + if (resultRelationDesc->rd_rel->relhasoids) + HeapTupleSetOid(tuple, InvalidOid); + ++ storeWritableSystemAttribute(resultRelationDesc, slot, tuple); ++ + /* BEFORE ROW INSERT Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) +@@ -1811,6 +1910,12 @@ ExecInsert(TupleTableSlot *slot, + } + + /* ++ * SELinux assigns default security label, and ++ * it also checks db_tuple:{insert} permission ++ */ ++ sepgsqlHeapTupleInsert(resultRelationDesc, tuple, false); ++ ++ /* + * Check the constraints of the tuple + */ + if (resultRelationDesc->rd_att->constr) +@@ -2018,6 +2123,8 @@ ExecUpdate(TupleTableSlot *slot, + resultRelInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelInfo->ri_RelationDesc; + ++ storeWritableSystemAttribute(resultRelationDesc, slot, tuple); ++ + /* BEFORE ROW UPDATE Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) +@@ -2048,6 +2155,9 @@ ExecUpdate(TupleTableSlot *slot, + } + } + ++ /* SELinux checks db_tuple:{relabelfrom relabelto}, if needed */ ++ sepgsqlHeapTupleUpdate(resultRelationDesc, tupleid, tuple); ++ + /* + * Check the constraints of the tuple + * +@@ -2843,6 +2953,7 @@ OpenIntoRel(QueryDesc *queryDesc) + Oid namespaceId; + Oid tablespaceId; + Datum reloptions; ++ Oid *secLabels; + AclResult aclresult; + Oid intoRelationId; + TupleDesc tupdesc; +@@ -2886,6 +2997,14 @@ OpenIntoRel(QueryDesc *queryDesc) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceId)); + ++ /* SELinux checks */ ++ secLabels = sepgsql_relation_create(intoName, ++ RELKIND_RELATION, ++ queryDesc->tupDesc, ++ namespaceId, ++ NULL, NIL, ++ true, true); ++ + /* + * Select tablespace to use. If not specified, use default tablespace + * (which may in turn default to database's default). +@@ -2944,7 +3063,8 @@ OpenIntoRel(QueryDesc *queryDesc) + 0, + into->onCommit, + reloptions, +- allowSystemTableMods); ++ allowSystemTableMods, ++ secLabels); + + FreeTupleDesc(tupdesc); + +@@ -3069,6 +3189,10 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self) + if (myState->rel->rd_rel->relhasoids) + HeapTupleSetOid(tuple, InvalidOid); + ++ storeWritableSystemAttribute(myState->rel, slot, tuple); ++ /* SELinux checks db_tuple:{insert} */ ++ sepgsqlHeapTupleInsert(myState->rel, tuple, false); ++ + heap_insert(myState->rel, + tuple, + myState->estate->es_output_cid, +diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c +index 119ddbc..66f918e 100644 +--- a/src/backend/executor/execQual.c ++++ b/src/backend/executor/execQual.c +@@ -47,6 +47,7 @@ + #include "nodes/nodeFuncs.h" + #include "optimizer/planner.h" + #include "pgstat.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -1034,6 +1035,7 @@ init_fcache(Oid foid, FuncExprState *fcache, + aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); ++ sepgsql_proc_execute(foid); + + /* + * Safety check on nargs. Under normal circumstances this should never +@@ -4032,6 +4034,7 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(acoerce->elemfuncid)); ++ sepgsql_proc_execute(acoerce->elemfuncid); + + /* Set up the primary fmgr lookup information */ + fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), +diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c +index 19fa4e6..44021f2 100644 +--- a/src/backend/executor/execScan.c ++++ b/src/backend/executor/execScan.c +@@ -20,6 +20,7 @@ + + #include "executor/executor.h" + #include "miscadmin.h" ++#include "security/rowlevel.h" + #include "utils/memutils.h" + + +@@ -53,6 +54,7 @@ ExecScan(ScanState *node, + ProjectionInfo *projInfo; + ExprDoneCond isDone; + TupleTableSlot *resultSlot; ++ Scan *scan = (Scan *)node->ps.plan; + + /* + * Fetch data from node +@@ -64,7 +66,7 @@ ExecScan(ScanState *node, + * If we have neither a qual to check nor a projection to do, just skip + * all the overhead and return the raw scan tuple. + */ +- if (!qual && !projInfo) ++ if (!qual && !projInfo && !scan->rowlvPerms) + return (*accessMtd) (node); + + /* +@@ -128,9 +130,18 @@ ExecScan(ScanState *node, + * when the qual is nil ... saves only a few cycles, but they add up + * ... + */ +- if (!qual || ExecQual(qual, econtext, false)) ++ if (rowlvExecScanFilter(scan, node->ss_currentRelation, slot) ++ && (!qual || ExecQual(qual, econtext, false))) + { + /* ++ * NOTE: On FK checks, the Row-level feature needs to raise ++ * an error after evaluation of all the given quals to avoid ++ * incorrect error reporting. We assume FK implementation ++ * does not use malicious functions as the quals. ++ */ ++ rowlvExecScanAbort(scan, node->ss_currentRelation, slot); ++ ++ /* + * Found a satisfactory scan tuple. + */ + if (projInfo) +@@ -197,6 +208,7 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc + int numattrs = tupdesc->natts; + int attrno; + bool hasoid; ++ bool hassecid; + ListCell *tlist_item = list_head(tlist); + + /* Check the tlist attributes */ +@@ -240,12 +252,16 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc + return false; /* tlist too long */ + + /* +- * If the plan context requires a particular hasoid setting, then that has +- * to match, too. ++ * If the plan context requires a particular hasoid or hassecid setting, ++ * then that has to match, too. + */ + if (ExecContextForcesOids(ps, &hasoid) && + hasoid != tupdesc->tdhasoid) + return false; + ++ if (ExecContextForcesSecids(ps, &hassecid) && ++ hassecid != tupdesc->tdhassecid) ++ return false; ++ + return true; + } +diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c +index 06142c9..c5f614a 100644 +--- a/src/backend/executor/execTuples.c ++++ b/src/backend/executor/execTuples.c +@@ -100,7 +100,7 @@ + + + static TupleDesc ExecTypeFromTLInternal(List *targetList, +- bool hasoid, bool skipjunk); ++ bool hasoid, bool hasseclabel, bool skipjunk); + + + /* ---------------------------------------------------------------- +@@ -968,9 +968,9 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType) + * ---------------------------------------------------------------- + */ + TupleDesc +-ExecTypeFromTL(List *targetList, bool hasoid) ++ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid) + { +- return ExecTypeFromTLInternal(targetList, hasoid, false); ++ return ExecTypeFromTLInternal(targetList, hasoid, hassecid, false); + } + + /* ---------------------------------------------------------------- +@@ -980,13 +980,14 @@ ExecTypeFromTL(List *targetList, bool hasoid) + * ---------------------------------------------------------------- + */ + TupleDesc +-ExecCleanTypeFromTL(List *targetList, bool hasoid) ++ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid) + { +- return ExecTypeFromTLInternal(targetList, hasoid, true); ++ return ExecTypeFromTLInternal(targetList, hasoid, hassecid, true); + } + + static TupleDesc +-ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) ++ExecTypeFromTLInternal(List *targetList, bool hasoid, ++ bool hassecid, bool skipjunk) + { + TupleDesc typeInfo; + ListCell *l; +@@ -998,6 +999,7 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) + else + len = ExecTargetListLength(targetList); + typeInfo = CreateTemplateTupleDesc(len, hasoid); ++ typeInfo->tdhassecid = hassecid; + + foreach(l, targetList) + { +diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c +index 7033189..34faaeb 100644 +--- a/src/backend/executor/execUtils.c ++++ b/src/backend/executor/execUtils.c +@@ -512,6 +512,7 @@ void + ExecAssignResultTypeFromTL(PlanState *planstate) + { + bool hasoid; ++ bool hassecid; + TupleDesc tupDesc; + + if (ExecContextForcesOids(planstate, &hasoid)) +@@ -524,12 +525,15 @@ ExecAssignResultTypeFromTL(PlanState *planstate) + hasoid = false; + } + ++ if (!ExecContextForcesSecids(planstate, &hassecid)) ++ hassecid = false; ++ + /* + * ExecTypeFromTL needs the parse-time representation of the tlist, not a + * list of ExprStates. This is good because some plan nodes don't bother + * to set up planstate->targetlist ... + */ +- tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid); ++ tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid, hassecid); + ExecAssignResultType(planstate, tupDesc); + } + +diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c +index 1d679a9..8b46999 100644 +--- a/src/backend/executor/functions.c ++++ b/src/backend/executor/functions.c +@@ -1135,7 +1135,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, + + /* Set up junk filter if needed */ + if (junkFilter) +- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); ++ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + } + else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) + { +@@ -1167,7 +1167,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, + COERCE_DONTCARE); + /* Set up junk filter if needed */ + if (junkFilter) +- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); ++ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + return false; /* NOT returning whole tuple */ + } + } +@@ -1180,7 +1180,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, + * what the caller expects will happen at runtime. + */ + if (junkFilter) +- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); ++ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + return true; + } + Assert(tupdesc); +diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c +index d7cccc5..6ba72ca 100644 +--- a/src/backend/executor/nodeAgg.c ++++ b/src/backend/executor/nodeAgg.c +@@ -81,6 +81,7 @@ + #include "parser/parse_agg.h" + #include "parser/parse_coerce.h" + #include "parser/parse_oper.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -1431,6 +1432,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(aggref->aggfnoid)); ++ sepgsql_proc_execute(aggref->aggfnoid); + + peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; + peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; +@@ -1454,6 +1456,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(transfn_oid)); ++ sepgsql_proc_execute(transfn_oid); + if (OidIsValid(finalfn_oid)) + { + aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, +@@ -1461,6 +1464,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(finalfn_oid)); ++ sepgsql_proc_execute(finalfn_oid); + } + } + +diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c +index b6143e6..4394855 100644 +--- a/src/backend/executor/nodeMergejoin.c ++++ b/src/backend/executor/nodeMergejoin.c +@@ -98,6 +98,7 @@ + #include "executor/execdefs.h" + #include "executor/nodeMergejoin.h" + #include "miscadmin.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" +@@ -215,6 +216,7 @@ MJExamineQuals(List *mergeclauses, + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(cmpproc)); ++ sepgsql_proc_execute(cmpproc); + + /* Set up the fmgr lookup information */ + fmgr_info(cmpproc, &(clause->cmpfinfo)); +diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c +index aff7a63..41dede3 100644 +--- a/src/backend/executor/nodeSubplan.c ++++ b/src/backend/executor/nodeSubplan.c +@@ -869,7 +869,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) + * (hack alert!). The righthand expressions will be evaluated in our + * own innerecontext. + */ +- tupDesc = ExecTypeFromTL(leftptlist, false); ++ tupDesc = ExecTypeFromTL(leftptlist, false, false); + slot = ExecAllocTableSlot(tupTable); + ExecSetSlotDescriptor(slot, tupDesc); + sstate->projLeft = ExecBuildProjectionInfo(lefttlist, +@@ -877,7 +877,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) + slot, + NULL); + +- tupDesc = ExecTypeFromTL(rightptlist, false); ++ tupDesc = ExecTypeFromTL(rightptlist, false, false); + slot = ExecAllocTableSlot(tupTable); + ExecSetSlotDescriptor(slot, tupDesc); + sstate->projRight = ExecBuildProjectionInfo(righttlist, +diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c +index 6674f67..fec5c4a 100644 +--- a/src/backend/executor/nodeWindowAgg.c ++++ b/src/backend/executor/nodeWindowAgg.c +@@ -43,6 +43,7 @@ + #include "optimizer/clauses.h" + #include "parser/parse_agg.h" + #include "parser/parse_coerce.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/datum.h" +@@ -1224,6 +1225,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(wfunc->winfnoid)); ++ sepgsql_proc_execute(wfunc->winfnoid); + + /* Fill in the perfuncstate data */ + perfuncstate->wfuncstate = wfuncstate; +@@ -1418,6 +1420,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(transfn_oid)); ++ sepgsql_proc_execute(transfn_oid); + if (OidIsValid(finalfn_oid)) + { + aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, +@@ -1425,6 +1428,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(finalfn_oid)); ++ sepgsql_proc_execute(finalfn_oid); + } + } + +diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c +index 4cd7b0b..f661849 100644 +--- a/src/backend/executor/spi.c ++++ b/src/backend/executor/spi.c +@@ -705,6 +705,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, + mtuple->t_tableOid = tuple->t_tableOid; + if (rel->rd_att->tdhasoid) + HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); ++ if (HeapTupleHasSecid(mtuple)) ++ HeapTupleSetSecid(mtuple, HeapTupleGetSecid(tuple)); + } + else + { +diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c +index b6c46c1..6445ea8 100644 +--- a/src/backend/libpq/be-fsstubs.c ++++ b/src/backend/libpq/be-fsstubs.c +@@ -46,6 +46,7 @@ + #include "libpq/be-fsstubs.h" + #include "libpq/libpq-fs.h" + #include "miscadmin.h" ++#include "security/sepgsql.h" + #include "storage/fd.h" + #include "storage/large_object.h" + #include "utils/acl.h" +@@ -173,6 +174,9 @@ lo_read(int fd, char *buf, int len) + errmsg("permission denied for large object %u", + cookies[fd]->id))); + ++ /* SELinux db_blob:{read} checks */ ++ sepgsql_largeobject_read(cookies[fd]->id, cookies[fd]->snapshot); ++ + status = inv_read(cookies[fd], buf, len); + + return status; +@@ -205,6 +209,9 @@ lo_write(int fd, const char *buf, int len) + errmsg("permission denied for large object %u", + cookies[fd]->id))); + ++ /* SELinux db_blob:{write} */ ++ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot); ++ + status = inv_write(cookies[fd], buf, len); + + return status; +@@ -233,6 +240,10 @@ Datum + lo_creat(PG_FUNCTION_ARGS) + { + Oid lobjId; ++ Oid secid; ++ ++ /* SELinux: db_blob:{create} */ ++ secid = sepgsql_largeobject_create(InvalidOid, NULL); + + /* + * We don't actually need to store into fscxt, but create it anyway to +@@ -240,7 +251,7 @@ lo_creat(PG_FUNCTION_ARGS) + */ + CreateFSContext(); + +- lobjId = inv_create(InvalidOid); ++ lobjId = inv_create(InvalidOid, secid); + + PG_RETURN_OID(lobjId); + } +@@ -249,6 +260,10 @@ Datum + lo_create(PG_FUNCTION_ARGS) + { + Oid lobjId = PG_GETARG_OID(0); ++ Oid secid; ++ ++ /* SELinux: db_blob:{create} */ ++ secid = sepgsql_largeobject_create(lobjId, NULL); + + /* + * We don't actually need to store into fscxt, but create it anyway to +@@ -256,7 +271,7 @@ lo_create(PG_FUNCTION_ARGS) + */ + CreateFSContext(); + +- lobjId = inv_create(lobjId); ++ lobjId = inv_create(lobjId, secid); + + PG_RETURN_OID(lobjId); + } +@@ -286,6 +301,9 @@ lo_unlink(PG_FUNCTION_ARGS) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be owner of large object %u", lobjId))); + ++ /* SELinux: db_blob:{drop} */ ++ sepgsql_largeobject_drop(lobjId); ++ + /* + * If there are any open LO FDs referencing that ID, close 'em. + */ +@@ -381,9 +399,10 @@ lo_import_internal(text *filename, Oid lobjOid) + int nbytes, + tmp; + char buf[BUFSIZE]; +- char fnamebuf[MAXPGPATH]; ++ char *fnamebuf = text_to_cstring(filename); + LargeObjectDesc *lobj; + Oid oid; ++ Oid secid; + + #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS + if (!superuser()) +@@ -392,13 +411,14 @@ lo_import_internal(text *filename, Oid lobjOid) + errmsg("must be superuser to use server-side lo_import()"), + errhint("Anyone can use the client-side lo_import() provided by libpq."))); + #endif ++ /* SELinux: db_blob:{create import} */ ++ secid = sepgsql_largeobject_import(lobjOid, fnamebuf); + + CreateFSContext(); + + /* + * open the file to be read in + */ +- text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); + fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666); + if (fd < 0) + ereport(ERROR, +@@ -409,7 +429,7 @@ lo_import_internal(text *filename, Oid lobjOid) + /* + * create an inversion object + */ +- oid = inv_create(lobjOid); ++ oid = inv_create(lobjOid, secid); + + /* + * read in from the filesystem and write to the inversion object +@@ -447,7 +467,7 @@ lo_export(PG_FUNCTION_ARGS) + int nbytes, + tmp; + char buf[BUFSIZE]; +- char fnamebuf[MAXPGPATH]; ++ char *fnamebuf = text_to_cstring(filename); + LargeObjectDesc *lobj; + mode_t oumask; + +@@ -458,6 +478,8 @@ lo_export(PG_FUNCTION_ARGS) + errmsg("must be superuser to use server-side lo_export()"), + errhint("Anyone can use the client-side lo_export() provided by libpq."))); + #endif ++ /* SELinux: db_blob:{read export} */ ++ sepgsql_largeobject_export(lobjId, fnamebuf); + + CreateFSContext(); + +@@ -528,6 +550,9 @@ lo_truncate(PG_FUNCTION_ARGS) + errmsg("permission denied for large object %u", + cookies[fd]->id))); + ++ /* SELinux: db_blob:{write} */ ++ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot); ++ + inv_truncate(cookies[fd], len); + + PG_RETURN_INT32(0); +diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c +index 13c82a6..ca21326 100644 +--- a/src/backend/nodes/copyfuncs.c ++++ b/src/backend/nodes/copyfuncs.c +@@ -259,6 +259,7 @@ CopyScanFields(Scan *from, Scan *newnode) + CopyPlanFields((Plan *) from, (Plan *) newnode); + + COPY_SCALAR_FIELD(scanrelid); ++ COPY_SCALAR_FIELD(rowlvPerms); + } + + /* +@@ -2075,6 +2076,7 @@ _copyColumnDef(ColumnDef *from) + COPY_NODE_FIELD(raw_default); + COPY_NODE_FIELD(cooked_default); + COPY_NODE_FIELD(constraints); ++ COPY_NODE_FIELD(secLabel); + + return newnode; + } +@@ -2414,6 +2416,7 @@ _copyCreateStmt(CreateStmt *from) + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(oncommit); + COPY_STRING_FIELD(tablespacename); ++ COPY_NODE_FIELD(secLabel); + + return newnode; + } +@@ -2638,6 +2641,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) + return newnode; + } + ++static AlterSecLabelStmt * ++_copyAlterSecLabelStmt(AlterSecLabelStmt *from) ++{ ++ AlterSecLabelStmt *newnode = makeNode(AlterSecLabelStmt); ++ ++ COPY_SCALAR_FIELD(objectType); ++ COPY_NODE_FIELD(relation); ++ COPY_NODE_FIELD(object); ++ COPY_NODE_FIELD(objarg); ++ COPY_STRING_FIELD(subname); ++ COPY_NODE_FIELD(secLabel); ++ ++ return newnode; ++} ++ + static RuleStmt * + _copyRuleStmt(RuleStmt *from) + { +@@ -2887,6 +2905,7 @@ _copyCreateSeqStmt(CreateSeqStmt *from) + + COPY_NODE_FIELD(sequence); + COPY_NODE_FIELD(options); ++ COPY_NODE_FIELD(secLabel); + + return newnode; + } +@@ -3819,6 +3838,9 @@ copyObject(void *from) + case T_AlterOwnerStmt: + retval = _copyAlterOwnerStmt(from); + break; ++ case T_AlterSecLabelStmt: ++ retval = _copyAlterSecLabelStmt(from); ++ break; + case T_RuleStmt: + retval = _copyRuleStmt(from); + break; +diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c +index 06a06b3..0752771 100644 +--- a/src/backend/nodes/equalfuncs.c ++++ b/src/backend/nodes/equalfuncs.c +@@ -1078,6 +1078,7 @@ _equalCreateStmt(CreateStmt *a, CreateStmt *b) + COMPARE_NODE_FIELD(options); + COMPARE_SCALAR_FIELD(oncommit); + COMPARE_STRING_FIELD(tablespacename); ++ COMPARE_NODE_FIELD(secLabel); + + return true; + } +@@ -1271,6 +1272,19 @@ _equalAlterOwnerStmt(AlterOwnerStmt *a, AlterOwnerStmt *b) + } + + static bool ++_equalAlterSecLabelStmt(AlterSecLabelStmt *a, AlterSecLabelStmt *b) ++{ ++ COMPARE_SCALAR_FIELD(objectType); ++ COMPARE_NODE_FIELD(relation); ++ COMPARE_NODE_FIELD(object); ++ COMPARE_NODE_FIELD(objarg); ++ COMPARE_STRING_FIELD(subname); ++ COMPARE_NODE_FIELD(secLabel); ++ ++ return true; ++} ++ ++static bool + _equalRuleStmt(RuleStmt *a, RuleStmt *b) + { + COMPARE_NODE_FIELD(relation); +@@ -1477,6 +1491,7 @@ _equalCreateSeqStmt(CreateSeqStmt *a, CreateSeqStmt *b) + { + COMPARE_NODE_FIELD(sequence); + COMPARE_NODE_FIELD(options); ++ COMPARE_NODE_FIELD(secLabel); + + return true; + } +@@ -2054,6 +2069,7 @@ _equalColumnDef(ColumnDef *a, ColumnDef *b) + COMPARE_NODE_FIELD(raw_default); + COMPARE_NODE_FIELD(cooked_default); + COMPARE_NODE_FIELD(constraints); ++ COMPARE_NODE_FIELD(secLabel); + + return true; + } +@@ -2596,6 +2612,9 @@ equal(void *a, void *b) + case T_AlterOwnerStmt: + retval = _equalAlterOwnerStmt(a, b); + break; ++ case T_AlterSecLabelStmt: ++ retval = _equalAlterSecLabelStmt(a, b); ++ break; + case T_RuleStmt: + retval = _equalRuleStmt(a, b); + break; +diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c +index 1c8691a..ffc2f19 100644 +--- a/src/backend/nodes/outfuncs.c ++++ b/src/backend/nodes/outfuncs.c +@@ -285,6 +285,7 @@ _outScanInfo(StringInfo str, Scan *node) + _outPlanInfo(str, (Plan *) node); + + WRITE_UINT_FIELD(scanrelid); ++ WRITE_UINT_FIELD(rowlvPerms); + } + + /* +@@ -1534,6 +1535,7 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node) + WRITE_BOOL_FIELD(has_eclass_joins); + WRITE_BITMAPSET_FIELD(index_outer_relids); + WRITE_NODE_FIELD(index_inner_paths); ++ WRITE_UINT_FIELD(rowlvPerms); + } + + static void +@@ -1717,6 +1719,7 @@ _outCreateStmt(StringInfo str, CreateStmt *node) + WRITE_NODE_FIELD(options); + WRITE_ENUM_FIELD(oncommit, OnCommitAction); + WRITE_STRING_FIELD(tablespacename); ++ WRITE_NODE_FIELD(secLabel); + } + + static void +@@ -1839,6 +1842,7 @@ _outColumnDef(StringInfo str, ColumnDef *node) + WRITE_NODE_FIELD(raw_default); + WRITE_NODE_FIELD(cooked_default); + WRITE_NODE_FIELD(constraints); ++ WRITE_NODE_FIELD(secLabel); + } + + static void +diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c +index 6e5c251..8156719 100644 +--- a/src/backend/optimizer/plan/createplan.c ++++ b/src/backend/optimizer/plan/createplan.c +@@ -305,6 +305,9 @@ create_scan_plan(PlannerInfo *root, Path *best_path) + break; + } + ++ /* Copy of row-level permissions to Scan node */ ++ ((Scan *)plan)->rowlvPerms = rel->rowlvPerms; ++ + /* + * If there are any pseudoconstant clauses attached to this node, insert a + * gating Result node that evaluates the pseudoconstants as one-time +diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c +index be75590..694a40c 100644 +--- a/src/backend/optimizer/util/clauses.c ++++ b/src/backend/optimizer/util/clauses.c +@@ -38,6 +38,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_func.h" + #include "rewrite/rewriteManip.h" ++#include "security/sepgsql.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -3502,6 +3503,7 @@ inline_function(Oid funcid, Oid result_type, List *args, + funcform->prosecdef || + funcform->proretset || + !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || ++ !sepgsql_proc_hint_inlined(func_tuple) || + funcform->pronargs != list_length(args)) + return NULL; + +@@ -3970,6 +3972,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) + funcform->prosecdef || + !funcform->proretset || + !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || ++ !sepgsql_proc_hint_inlined(func_tuple) || + funcform->pronargs != list_length(fexpr->args)) + { + ReleaseSysCache(func_tuple); +diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c +index 1d93203..3d3c455 100644 +--- a/src/backend/optimizer/util/relnode.c ++++ b/src/backend/optimizer/util/relnode.c +@@ -21,6 +21,7 @@ + #include "optimizer/plancat.h" + #include "optimizer/restrictinfo.h" + #include "parser/parsetree.h" ++#include "security/rowlevel.h" + #include "utils/hsearch.h" + + +@@ -91,6 +92,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) + rel->has_eclass_joins = false; + rel->index_outer_relids = NULL; + rel->index_inner_paths = NIL; ++ rel->rowlvPerms = rowlvSetupPermissions(rte); + + /* Check type of rtable entry */ + switch (rte->rtekind) +diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c +index f110463..a236e3c 100644 +--- a/src/backend/parser/analyze.c ++++ b/src/backend/parser/analyze.c +@@ -25,6 +25,7 @@ + #include "postgres.h" + + #include "access/sysattr.h" ++#include "catalog/heap.h" + #include "catalog/pg_type.h" + #include "nodes/makefuncs.h" + #include "nodes/nodeFuncs.h" +@@ -660,7 +661,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) + tle = makeTargetEntry(expr, + attr_num, + col->name, +- false); ++ attr_num < 0 ? true : false); + qry->targetList = lappend(qry->targetList, tle); + + rte->modifiedCols = bms_add_member(rte->modifiedCols, +@@ -775,6 +776,48 @@ transformInsertRow(ParseState *pstate, List *exprlist, + return result; + } + ++static void ++transformSelectIntoSystemColumn(ParseState *pstate, Query *qry) ++{ ++ ListCell *l; ++ uint32 system_attrs = 0; ++ bool relhasoids ++ = interpretOidsOption(qry->intoClause->options); ++ ++ foreach (l, qry->targetList) ++ { ++ Form_pg_attribute attr; ++ TargetEntry *tle = lfirst(l); ++ ++ if (tle->resjunk) ++ continue; ++ ++ attr = SystemAttributeByName(tle->resname, relhasoids); ++ if (attr && SystemAttributeIsWritable(attr->attnum)) ++ { ++ uint32 mask = (1<<(-attr->attnum)); ++ ++ /* duplication checks */ ++ if (system_attrs & mask) ++ continue; ++ system_attrs |= mask; ++ ++ if (exprType((Node *) tle->expr) != attr->atttypid) ++ { ++ tle->expr = ++ (Expr *) coerce_to_target_type(pstate, ++ (Node *) tle->expr, ++ exprType((Node *) tle->expr), ++ attr->atttypid, ++ attr->atttypmod, ++ COERCION_IMPLICIT, ++ COERCE_IMPLICIT_CAST, ++ -1); ++ } ++ tle->resjunk = true; ++ } ++ } ++} + + /* + * transformSelectStmt - +@@ -879,6 +922,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) + if (stmt->intoClause) + { + qry->intoClause = stmt->intoClause; ++ transformSelectIntoSystemColumn(pstate, qry); + if (stmt->intoClause->colNames) + applyColumnNames(qry->targetList, stmt->intoClause->colNames); + } +diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y +index d13b0f0..80a1f4a 100644 +--- a/src/backend/parser/gram.y ++++ b/src/backend/parser/gram.y +@@ -58,6 +58,7 @@ + #include "nodes/makefuncs.h" + #include "nodes/nodeFuncs.h" + #include "parser/gramparse.h" ++#include "security/sepgsql.h" + #include "storage/lmgr.h" + #include "utils/date.h" + #include "utils/datetime.h" +@@ -184,7 +185,7 @@ static TypeName *TableFuncTypeName(List *columns); + %type stmt schema_stmt + AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt + AlterForeignServerStmt AlterGroupStmt +- AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt ++ AlterObjectSchemaStmt AlterOwnerStmt AlterSecLabelStmt AlterSeqStmt AlterTableStmt + AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt + AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt + ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt +@@ -402,6 +403,10 @@ static TypeName *TableFuncTypeName(List *columns); + %type OptTableSpace OptConsTableSpace OptTableSpaceOwner + %type opt_check_option + ++%type OptSecLabel SecLabelItem SecLabelToItem ++%type OptTableSecLabel TableSecLabelList ++%type TableSecLabelItem ++ + %type xml_attribute_el + %type xml_attribute_list xml_attributes + %type xml_root_version opt_xml_root_standalone +@@ -437,7 +442,7 @@ static TypeName *TableFuncTypeName(List *columns); + CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE + CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT + COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS +- CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB ++ CONTENT_P CONTEXT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB + CREATEROLE CREATEUSER CROSS CSV CURRENT_P + CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA + CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE +@@ -608,6 +613,7 @@ stmt : + | AlterGroupStmt + | AlterObjectSchemaStmt + | AlterOwnerStmt ++ | AlterSecLabelStmt + | AlterSeqStmt + | AlterTableStmt + | AlterRoleSetStmt +@@ -1042,7 +1048,7 @@ DropGroupStmt: + *****************************************************************************/ + + CreateSchemaStmt: +- CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSchemaEltList ++ CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSecLabel OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* One can omit the schema name or the authorization id. */ +@@ -1051,16 +1057,18 @@ CreateSchemaStmt: + else + n->schemaname = $5; + n->authid = $5; +- n->schemaElts = $6; ++ n->secLabel = $6; ++ n->schemaElts = $7; + $$ = (Node *)n; + } +- | CREATE SCHEMA ColId OptSchemaEltList ++ | CREATE SCHEMA ColId OptSecLabel OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* ...but not both */ + n->schemaname = $3; + n->authid = NULL; +- n->schemaElts = $4; ++ n->secLabel = $4; ++ n->schemaElts = $5; + $$ = (Node *)n; + } + ; +@@ -2037,7 +2045,7 @@ opt_using: + *****************************************************************************/ + + CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' +- OptInherit OptWith OnCommitOption OptTableSpace ++ OptInherit OptWith OnCommitOption OptTableSpace OptTableSecLabel + { + CreateStmt *n = makeNode(CreateStmt); + $4->istemp = $2; +@@ -2048,10 +2056,11 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' + n->options = $9; + n->oncommit = $10; + n->tablespacename = $11; ++ n->secLabel = $12; + $$ = (Node *)n; + } + | CREATE OptTemp TABLE qualified_name OF qualified_name +- '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace ++ '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace OptTableSecLabel + { + /* SQL99 CREATE TABLE OF (cols) seems to be satisfied + * by our inheritance capabilities. Let's try it... +@@ -2065,6 +2074,7 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' + n->options = $10; + n->oncommit = $11; + n->tablespacename = $12; ++ n->secLabel = $13; + $$ = (Node *)n; + } + ; +@@ -2114,6 +2124,7 @@ columnDef: ColId Typename ColQualList + n->typename = $2; + n->constraints = $3; + n->is_local = true; ++ n->secLabel = NULL; + $$ = (Node *)n; + } + ; +@@ -2585,12 +2596,13 @@ opt_with_data: + *****************************************************************************/ + + CreateSeqStmt: +- CREATE OptTemp SEQUENCE qualified_name OptSeqOptList ++ CREATE OptTemp SEQUENCE qualified_name OptSeqOptList OptSecLabel + { + CreateSeqStmt *n = makeNode(CreateSeqStmt); + $4->istemp = $2; + n->sequence = $4; + n->options = $5; ++ n->secLabel = $6; + $$ = (Node *)n; + } + ; +@@ -4893,6 +4905,10 @@ createfunc_opt_item: + { + $$ = makeDefElem("window", (Node *)makeInteger(TRUE)); + } ++ | SecLabelItem ++ { ++ $$ = makeDefElem("security_context", $1); ++ } + | common_func_opt_item + { + $$ = $1; +@@ -5607,6 +5623,101 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId + } + ; + ++/***************************************************************************** ++ * ++ * ALTER THING name SECURITY CONTEXT TO ++ * ++ *****************************************************************************/ ++ ++AlterSecLabelStmt: ALTER DATABASE database_name SecLabelToItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_DATABASE; ++ n->object = list_make1(makeString($3)); ++ n->secLabel = $4; ++ $$ = (Node *) n; ++ } ++ | ALTER SCHEMA name SecLabelToItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_SCHEMA; ++ n->object = list_make1(makeString($3)); ++ n->secLabel = $4; ++ $$ = (Node *) n; ++ } ++ | ALTER TABLE relation_expr SecLabelToItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_TABLE; ++ n->relation = $3; ++ n->secLabel = $4; ++ $$ = (Node *) n; ++ } ++ | ALTER TABLE relation_expr ALTER opt_column ColId SecLabelToItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_COLUMN; ++ n->relation = $3; ++ n->subname = $6; ++ n->secLabel = $7; ++ $$ = (Node *) n; ++ } ++ | ALTER SEQUENCE relation_expr SecLabelToItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_SEQUENCE; ++ n->relation = $3; ++ n->secLabel = $4; ++ $$ = (Node *) n; ++ } ++ | ALTER FUNCTION function_with_argtypes SecLabelToItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_FUNCTION; ++ n->object = $3->funcname; ++ n->objarg = $3->funcargs; ++ n->secLabel = $4; ++ $$ = (Node *) n; ++ } ++ | ALTER LARGE_P OBJECT_P Iconst SecLabelToItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_LARGEOBJECT; ++ n->object = list_make1(makeInteger($4)); ++ n->secLabel = $5; ++ $$ = (Node *) n; ++ } ++ ; ++ ++OptTableSecLabel: SECURITY CONTEXT_P '(' TableSecLabelList ')' { $$ = $4; } ++ | /* EMPTY */ { $$ = NIL; } ++ ; ++ ++TableSecLabelList: TableSecLabelItem { $$ = list_make1($1); } ++ | TableSecLabelList ',' TableSecLabelItem { $$ = lappend($1, $3); } ++ ; ++ ++TableSecLabelItem: Sconst ++ { $$ = makeDefElem(NULL, (Node *)makeString($1)); } ++ | ColId '=' Sconst ++ { $$ = makeDefElem($1, (Node *)makeString($3)); } ++ ; ++ ++OptSecLabel: SecLabelItem { $$ = $1; } ++ | /* EMPTY */ { $$ = NULL; } ++ ; ++ ++SecLabelItem: SECURITY CONTEXT_P '(' Sconst ')' ++ { ++ $$ = (Node *) makeString($4); ++ } ++ ; ++ ++SecLabelToItem: SECURITY CONTEXT_P TO Sconst ++ { ++ $$ = (Node *) makeString($4); ++ } ++ ; + + /***************************************************************************** + * +@@ -6049,6 +6160,10 @@ createdb_opt_item: + { + $$ = makeDefElem("owner", NULL); + } ++ | SecLabelItem ++ { ++ $$ = makeDefElem("security_context", $1); ++ } + ; + + /* +@@ -10175,6 +10290,7 @@ unreserved_keyword: + | CONNECTION + | CONSTRAINTS + | CONTENT_P ++ | CONTEXT_P + | CONTINUE_P + | CONVERSION_P + | COPY +diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c +index 08b8edb..97aa7aa 100644 +--- a/src/backend/parser/parse_target.c ++++ b/src/backend/parser/parse_target.c +@@ -14,6 +14,7 @@ + */ + #include "postgres.h" + ++#include "catalog/heap.h" + #include "catalog/pg_type.h" + #include "commands/dbcommands.h" + #include "funcapi.h" +@@ -361,16 +362,33 @@ transformAssignedExpr(ParseState *pstate, + Oid attrtype; /* type of target column */ + int32 attrtypmod; + Relation rd = pstate->p_target_relation; ++ bool relhasoids = RelationGetForm(rd)->relhasoids; + + Assert(rd != NULL); +- if (attrno <= 0) +- ereport(ERROR, +- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), +- errmsg("cannot assign to system column \"%s\"", +- colname), +- parser_errposition(pstate, location))); +- attrtype = attnumTypeId(rd, attrno); +- attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ++ if (attrno > 0) ++ { ++ attrtype = attnumTypeId(rd, attrno); ++ attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ++ } ++ else ++ { ++ Form_pg_attribute attForm ++ = SystemAttributeDefinition(attrno, relhasoids); ++ if (attForm && SystemAttributeIsWritable(attrno)) ++ { ++ attrtype = attForm->atttypid; ++ attrtypmod = attForm->atttypmod; ++ } ++ else ++ { ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("cannot assign to system column \"%s\"", ++ colname), ++ parser_errposition(pstate, location))); ++ return NULL; /* compiler kindness */ ++ } ++ } + + /* + * If the expression is a DEFAULT placeholder, insert the attribute's +@@ -515,6 +533,9 @@ updateTargetListEntry(ParseState *pstate, + */ + tle->resno = (AttrNumber) attrno; + tle->resname = colname; ++ ++ if (SystemAttributeIsWritable(attrno)) ++ tle->resjunk = true; + } + + +@@ -789,6 +810,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) + Bitmapset *wholecols = NULL; + Bitmapset *partialcols = NULL; + ListCell *tl; ++ uint32 system_attrs = 0UL; + + foreach(tl, cols) + { +@@ -797,14 +819,37 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) + int attrno; + + /* Lookup column name, ereport on failure */ +- attrno = attnameAttNum(pstate->p_target_relation, name, false); ++ attrno = attnameAttNum(pstate->p_target_relation, name, true); + if (attrno == InvalidAttrNumber) ++ { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + name, + RelationGetRelationName(pstate->p_target_relation)), + parser_errposition(pstate, col->location))); ++ } ++ else if (attrno < 0) ++ { ++ if (SystemAttributeIsWritable(attrno)) ++ { ++ uint32 mask = (1<<(-attrno)); ++ ++ if ((system_attrs & mask) != 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_DUPLICATE_COLUMN), ++ errmsg("column \"%s\" specified more than once", name), ++ parser_errposition(pstate, col->location))); ++ system_attrs |= mask; ++ *attrnos = lappend_int(*attrnos, attrno); ++ continue; ++ } ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), ++ errmsg("column \"%s\" of relation \"%s\" is system column", ++ name, RelationGetRelationName(pstate->p_target_relation)), ++ parser_errposition(pstate, col->location))); ++ } + + /* + * Check for duplicates, but only of whole columns --- we allow +diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c +index 1a9e387..c5ea995 100644 +--- a/src/backend/parser/parse_utilcmd.c ++++ b/src/backend/parser/parse_utilcmd.c +@@ -49,6 +49,7 @@ + #include "parser/parse_type.h" + #include "parser/parse_utilcmd.h" + #include "rewrite/rewriteManip.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -565,6 +566,8 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(relation)); ++ /* SELinux checks */ ++ sepgsql_relation_copy_definition(RelationGetRelid(relation)); + + tupleDesc = RelationGetDescr(relation); + constr = tupleDesc->constr; +diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c +index 3e1a056..48d0642 100644 +--- a/src/backend/postmaster/autovacuum.c ++++ b/src/backend/postmaster/autovacuum.c +@@ -2004,7 +2004,7 @@ do_autovacuum(void) + object.classId = RelationRelationId; + object.objectId = relid; + object.objectSubId = 0; +- performDeletion(&object, DROP_CASCADE); ++ performDeletionNoPerms(&object, DROP_CASCADE); + } + else + { +diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c +index d63214b..e88b469 100644 +--- a/src/backend/postmaster/postmaster.c ++++ b/src/backend/postmaster/postmaster.c +@@ -108,6 +108,7 @@ + #include "postmaster/pgarch.h" + #include "postmaster/postmaster.h" + #include "postmaster/syslogger.h" ++#include "security/sepgsql.h" + #include "storage/fd.h" + #include "storage/ipc.h" + #include "storage/pg_shmem.h" +@@ -209,7 +210,8 @@ static pid_t StartupPID = 0, + AutoVacPID = 0, + PgArchPID = 0, + PgStatPID = 0, +- SysLoggerPID = 0; ++ SysLoggerPID = 0, ++ sepgsqlReceiverPID = 0; + + /* Startup/shutdown state */ + #define NoShutdown 0 +@@ -445,6 +447,7 @@ static void ShmemBackendArrayRemove(Backend *bn); + #define StartupDataBase() StartChildProcess(StartupProcess) + #define StartBackgroundWriter() StartChildProcess(BgWriterProcess) + #define StartWalWriter() StartChildProcess(WalWriterProcess) ++#define StartSELinuxReceiver() StartChildProcess(SelinuxReceiverProcess) + + /* Macros to check exit status of a child process */ + #define EXIT_STATUS_0(st) ((st) == 0) +@@ -1436,6 +1439,11 @@ ServerLoop(void) + if (PgStatPID == 0 && pmState == PM_RUN) + PgStatPID = pgstat_start(); + ++ /* if we have lost the selinux netlink receiver, try to start */ ++ if (sepgsqlReceiverPID == 0 && pmState == PM_RUN && ++ sepgsqlReceiverStart()) ++ sepgsqlReceiverPID = StartSELinuxReceiver(); ++ + /* If we need to signal the autovacuum launcher, do so now */ + if (avlauncher_needs_signal) + { +@@ -2055,6 +2063,8 @@ SIGHUP_handler(SIGNAL_ARGS) + signal_child(SysLoggerPID, SIGHUP); + if (PgStatPID != 0) + signal_child(PgStatPID, SIGHUP); ++ if (sepgsqlReceiverPID != 0) ++ signal_child(sepgsqlReceiverPID, SIGHUP); + + /* Reload authentication config files too */ + if (!load_hba()) +@@ -2115,6 +2125,9 @@ pmdie(SIGNAL_ARGS) + /* and the walwriter too */ + if (WalWriterPID != 0) + signal_child(WalWriterPID, SIGTERM); ++ /* and the selinux netlink receiver too */ ++ if (sepgsqlReceiverPID != 0) ++ signal_child(sepgsqlReceiverPID, SIGTERM); + pmState = PM_WAIT_BACKUP; + } + +@@ -2162,6 +2175,9 @@ pmdie(SIGNAL_ARGS) + /* and the walwriter too */ + if (WalWriterPID != 0) + signal_child(WalWriterPID, SIGTERM); ++ /* and the selinux netlink receiver too */ ++ if (sepgsqlReceiverPID != 0) ++ signal_child(sepgsqlReceiverPID, SIGTERM); + pmState = PM_WAIT_BACKENDS; + } + +@@ -2195,6 +2211,8 @@ pmdie(SIGNAL_ARGS) + signal_child(PgArchPID, SIGQUIT); + if (PgStatPID != 0) + signal_child(PgStatPID, SIGQUIT); ++ if (sepgsqlReceiverPID != 0) ++ signal_child(sepgsqlReceiverPID, SIGQUIT); + ExitPostmaster(0); + break; + } +@@ -2457,6 +2475,16 @@ reaper(SIGNAL_ARGS) + continue; + } + ++ /* Was it the selinux netlink receiver process? */ ++ if (pid == sepgsqlReceiverPID) ++ { ++ sepgsqlReceiverPID = 0; ++ if (!EXIT_STATUS_0(exitstatus)) ++ LogChildExit(LOG, _("SELinux netlink receiver process"), ++ pid, exitstatus); ++ continue; ++ } ++ + /* + * Else do standard backend child cleanup. + */ +@@ -2648,6 +2676,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) + signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); + } + ++ /* Take care of the selinux netlink receiver too */ ++ if (pid == sepgsqlReceiverPID) ++ sepgsqlReceiverPID = 0; ++ else if (sepgsqlReceiverPID != 0 && !FatalError) ++ { ++ ereport(DEBUG2, ++ (errmsg_internal("sending %s to process %d", ++ (SendStop ? "SIGSTOP" : "SIGQUIT"), ++ (int) sepgsqlReceiverPID))); ++ signal_child(sepgsqlReceiverPID, (SendStop ? SIGSTOP : SIGQUIT)); ++ } ++ + /* + * Force a power-cycle of the pgarch process too. (This isn't absolutely + * necessary, but it seems like a good idea for robustness, and it +@@ -2780,7 +2820,8 @@ PostmasterStateMachine(void) + StartupPID == 0 && + (BgWriterPID == 0 || !FatalError) && + WalWriterPID == 0 && +- AutoVacPID == 0) ++ AutoVacPID == 0 && ++ sepgsqlReceiverPID == 0) + { + if (FatalError) + { +@@ -4323,6 +4364,12 @@ StartChildProcess(AuxProcType type) + ereport(LOG, + (errmsg("could not fork WAL writer process: %m"))); + break; ++#ifdef HAVE_SELINUX ++ case SelinuxReceiverProcess: ++ ereport(LOG, ++ (errmsg("could not fork selinux receiver process: %m"))); ++ break; ++#endif + default: + ereport(LOG, + (errmsg("could not fork process: %m"))); +diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c +index 0b4f279..5fe6fd0 100644 +--- a/src/backend/rewrite/rewriteDefine.c ++++ b/src/backend/rewrite/rewriteDefine.c +@@ -27,6 +27,7 @@ + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteManip.h" + #include "rewrite/rewriteSupport.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/inval.h" +@@ -266,6 +267,9 @@ DefineQueryRewrite(char *rulename, + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(event_relation)); + ++ /* SELinux checks */ ++ sepgsql_rule_create(event_relid, rulename); ++ + /* + * No rule actions that modify OLD or NEW + */ +diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c +index a65b020..b967838 100644 +--- a/src/backend/rewrite/rewriteRemove.c ++++ b/src/backend/rewrite/rewriteRemove.c +@@ -22,6 +22,7 @@ + #include "catalog/pg_rewrite.h" + #include "miscadmin.h" + #include "rewrite/rewriteRemove.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/fmgroids.h" + #include "utils/inval.h" +@@ -78,6 +79,9 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(eventRelationOid)); + ++ /* SELinux checks */ ++ sepgsql_rule_drop(eventRelationOid, ruleName); ++ + /* + * Do the deletion + */ +diff --git a/src/backend/security/Makefile b/src/backend/security/Makefile +new file mode 100644 +index 0000000..23e51d6 +--- /dev/null ++++ b/src/backend/security/Makefile +@@ -0,0 +1,13 @@ ++# ++# Makefile for the enhanced security subsystem ++# ++ ++subdir = src/backend/security ++top_builddir = ../../.. ++include $(top_builddir)/src/Makefile.global ++ ++SUBDIRS = sepgsql ++ ++OBJS = rowlevel.o ++ ++include $(top_srcdir)/src/backend/common.mk +diff --git a/src/backend/security/rowlevel.c b/src/backend/security/rowlevel.c +new file mode 100644 +index 0000000..e08d4cc +--- /dev/null ++++ b/src/backend/security/rowlevel.c +@@ -0,0 +1,121 @@ ++/* ++ * src/backend/security/common.c ++ * common facilities for row-level access controls both of DAC and MAC ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_security.h" ++#include "security/rowlevel.h" ++#include "security/sepgsql.h" ++#include "storage/bufmgr.h" ++#include "storage/bufpage.h" ++#include "utils/rel.h" ++#include "utils/tqual.h" ++ ++/* ++ * rowlvGetPerformingMode ++ * rowlvSetPerformingMode ++ * enables to control the behavior of row-level features ++ * when violated tuples are detected. ++ * The default is ROWLV_FILTER_MODE which filters out ++ * violated tuples from result set, ROWLV_ABORT_MODE ++ * raises an error and ROWLV_BYPASS_MODE do nothing. ++ */ ++static int rowlv_mode = ROWLV_FILTER_MODE; ++ ++int rowlvGetPerformingMode(void) ++{ ++ return rowlv_mode; ++} ++ ++int rowlvSetPerformingMode(int new_mode) ++{ ++ int old_mode = new_mode; ++ ++ rowlv_mode = new_mode; ++ ++ return old_mode; ++} ++ ++/* ++ * rowlvSetupPermissions ++ * setups permissions for row-level access controls. ++ */ ++uint32 ++rowlvSetupPermissions(RangeTblEntry *rte) ++{ ++ return sepgsqlSetupTuplePerms(rte); ++} ++ ++/* ++ * rowlvExecScan ++ * a hook to filter out invisible/untouchable tuples. ++ */ ++static bool ++rowlvExecScan(Scan *scan, Relation rel, TupleTableSlot *slot, bool abort) ++{ ++ HeapTuple tuple; ++ uint32 perms = scan->rowlvPerms; ++ ++ if (!perms) ++ return true; ++ ++ tuple = ExecMaterializeSlot(slot); ++ ++ return sepgsqlExecScan(rel, tuple, perms, abort); ++} ++ ++bool ++rowlvExecScanFilter(Scan *scan, Relation rel, TupleTableSlot *slot) ++{ ++ if (!rel || !scan->rowlvPerms || rowlv_mode != ROWLV_FILTER_MODE) ++ return true; ++ ++ return rowlvExecScan(scan, rel, slot, false); ++} ++ ++void ++rowlvExecScanAbort(Scan *scan, Relation rel, TupleTableSlot *slot) ++{ ++ if (!rel || !scan->rowlvPerms || rowlv_mode != ROWLV_ABORT_MODE) ++ return; ++ ++ rowlvExecScan(scan, rel, slot, true); ++} ++ ++/* ++ * rowlvCopyToTuple ++ * checks permission on fetched tuple ++ */ ++bool ++rowlvCopyToTuple(Relation rel, HeapTuple tuple) ++{ ++ if (!sepgsqlExecScan(rel, tuple, SEPG_DB_TUPLE__SELECT, false)) ++ return false; ++ ++ return true; ++} ++ ++/* ++ * rowlvHeapTupleInsert ++ * assign default security attribute, and check permission ++ * if necessary. ++ */ ++void ++rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal) ++{ ++ sepgsqlHeapTupleInsert(rel, newtup, internal); ++} ++ ++/* ++ * rowlvHeapTupleUpdate ++ * check permission to change security attribute, if necesary ++ */ ++void ++rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup) ++{ ++ sepgsqlHeapTupleUpdate(rel, otid, newtup); ++} +diff --git a/src/backend/security/sepgsql/Makefile b/src/backend/security/sepgsql/Makefile +new file mode 100644 +index 0000000..2417aae +--- /dev/null ++++ b/src/backend/security/sepgsql/Makefile +@@ -0,0 +1,15 @@ ++# ++# Makefile ++# Makefile for utils/sepgsql : SE-PostgreSQL ++# ++ ++subdir = src/backend/security/sepgsql ++top_builddir = ../../../.. ++include $(top_builddir)/src/Makefile.global ++ ++OBJS = misc.o ++ifeq ($(enable_selinux), yes) ++OBJS += selinux.o checker.o bridge.o label.o ++endif ++ ++include $(top_srcdir)/src/backend/common.mk +diff --git a/src/backend/security/sepgsql/avc.c b/src/backend/security/sepgsql/avc.c +new file mode 100644 +index 0000000..8a88bcb +--- /dev/null ++++ b/src/backend/security/sepgsql/avc.c +@@ -0,0 +1,881 @@ ++/* ++ * src/backend/security/sepgsql/avc.c ++ * SE-PostgreSQL userspace access vector cache ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++ ++#include "access/hash.h" ++#include "catalog/pg_security.h" ++#include "libpq/pqsignal.h" ++#include "miscadmin.h" ++#include "postmaster/postmaster.h" ++#include "security/sepgsql.h" ++#include "storage/ipc.h" ++#include "storage/lwlock.h" ++#include "utils/memutils.h" ++#include ++#include ++#include ++ ++/* ++ * AVC: userspace access vector cache ++ * ++ * SE-PostgreSQL asks in-kernel SELinux to make its decision whether ++ * the required accesses should be allowed, or not, based on the unified ++ * security policy. It needs a system call invocation to communicate ++ * a kernel feature, such as SELinux, but it is a heavy task in most cases ++ * due to the context switching. ++ * ++ * The userspace avc enables to minimize the number of system call ++ * invocations, using a chache mechanim for the certain pair of security ++ * contexts and object classes (it means the kind of actions). ++ * It enables to hold recently fetched results from the in-kernel SELinux, ++ * and make a decision without context switching, if the cache hit. ++ * ++ * When the state of security policy is changed, the cached results ++ * shall to be invalidated. The state monitoring process launched by ++ * postmaster can receives the notification messages from the kernel ++ * space, and invalidate the current version of avc. ++ */ ++static MemoryContext AvcMemCtx = NULL; ++ ++#define AVC_HASH_NUM_SLOTS 256 ++#define AVC_HASH_NUM_NODES 180 ++ ++#define AVC_DATUM_NSID_SLOTS 19 ++typedef struct ++{ ++ uint32 hash_key; ++ ++ security_class_t tclass; ++ sepgsql_sid_t tsid; ++ sepgsql_sid_t nsid[AVC_DATUM_NSID_SLOTS]; ++ ++ access_vector_t allowed; ++ access_vector_t decided; ++ access_vector_t auditallow; ++ access_vector_t auditdeny; ++ ++ bool hot_cache; ++ bool permissive; ++ ++ char ncontext[1]; ++} avc_datum; ++ ++typedef struct avc_page ++{ ++ struct avc_page *next; ++ ++ security_context_t scontext; ++ ++ List *slot[AVC_HASH_NUM_SLOTS]; ++ ++ uint32 avc_count; ++ uint32 lru_hint; ++} avc_page; ++ ++static avc_page *current_page = NULL; ++ ++static int avc_version; ++ ++/* ++ * selinux_state ++ * ++ * It is deployed on the shared memory region, to show the system ++ * state of SELinux and its security policy. ++ * ++ * The selinux_state->version should be checked prior to avc accesses. ++ * If it does not match with the local avc_version, it means that ++ * system security policy was reloaded or system state (enforcing ++ * or permissive) was changed. ++ * ++ * The state monitoring worker process receives messages from the ++ * kernel using libselinux, and it updates the selinux_state. ++ */ ++struct ++{ ++ int version; ++ ++ bool enforcing; ++ ++} *selinux_state = NULL; ++ ++Size ++sepgsqlShmemSize(void) ++{ ++ if (!sepgsqlIsEnabled()) ++ return 0; ++ ++ return sizeof(*selinux_state); ++} ++ ++/* ++ * sepgsql_shmem_init ++ * attaches shared memory segment. ++ */ ++static void ++sepgsqlShmemInit(void) ++{ ++ bool found; ++ ++ selinux_state = ShmemInitStruct("SELinux policy state", ++ sepgsqlShmemSize(), &found); ++ if (!found) ++ { ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ ++ selinux_state->version = 0; ++ selinux_state->enforcing = (security_getenforce() > 0); ++ ++ LWLockRelease(SepgsqlAvcLock); ++ } ++} ++ ++/* ++ * sepgsqlAvcReset ++ * ++ * It invalidate access vector cache. It has to be called on errors, ++ * because avc entries for newly created context is uncertain whether ++ * it is still valid, or not. ++ * If error happens before avc initialization, we simply skip it. ++ */ ++void ++sepgsqlAvcReset(void) ++{ ++ if (!sepgsqlIsEnabled() || !AvcMemCtx) ++ return; ++ ++ MemoryContextReset(AvcMemCtx); ++ ++ current_page = NULL; ++ ++ sepgsqlAvcSwitchClient(sepgsqlGetClientLabel()); ++} ++ ++/* ++ * sepgsqlAvcCheckValid ++ * ++ * It checks whether the current AVC pages are valid, or not. ++ * If state monitoring process already received an invalidation ++ * message from the kernel, it clears current AVC pages and ++ * returns false. ++ */ ++static bool ++sepgsqlAvcCheckValid(void) ++{ ++ bool result = true; ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); ++ if (avc_version != selinux_state->version) ++ { ++ /* reset invalid avc pages, and makes an empty one */ ++ MemoryContextReset(AvcMemCtx); ++ ++ current_page = NULL; ++ ++ sepgsqlAvcSwitchClient(sepgsqlGetClientLabel()); ++ ++ /* copy current version to local */ ++ avc_version = selinux_state->version; ++ ++ result = false; ++ } ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return result; ++} ++ ++/* ++ * sepgsqlAvcInitialize ++ * ++ * It allocates a memory context for userspace AVC, ++ * map shared memory segment, and initialize avc_page ++ * for the current client's privilege. ++ * ++ * If the current backend is not associated with a certain ++ * client process, it switches to permissive mode to avoid ++ * to prevent any internal processes. ++ */ ++void ++sepgsqlAvcInitialize(void) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* ++ * local memory context ++ */ ++ AvcMemCtx = AllocSetContextCreate(TopMemoryContext, ++ "SE-PostgreSQL userspace avc", ++ ALLOCSET_DEFAULT_MINSIZE, ++ ALLOCSET_DEFAULT_INITSIZE, ++ ALLOCSET_DEFAULT_MAXSIZE); ++ sepgsqlShmemInit(); ++ ++ /* ++ * Switch to local permissive mode ++ */ ++ if (!MyProcPort) ++ sepgsqlSetEnforce(0); ++ ++ /* ++ * selinux_state->version is never negative value, ++ * so this call always reset local avc. ++ */ ++ avc_version = -1; ++ sepgsqlAvcCheckValid(); ++} ++ ++/* ++ * sepgsqlGetEnforce ++ * sepgsqlSetEnforce ++ * ++ * SELinux has two working mode called Enforcing/Permissive. ++ * In enforcing mode, it checks security policy and actually ++ * applies its access controls. In permissive mode, it also ++ * checks security policy, but does not apply any access ++ * controls. It is used to collect access denied logs to ++ * debug security policy. ++ * ++ * sepgsqlGetEnforce() returns the current working mode, and ++ * sepgsqlSetEnforce() switches the current working mode ++ * temporary. When we switches the mode, any errors have to ++ * be acquired, and it should be restored correctly. ++ */ ++static int local_enforce = -1; /* undefined */ ++ ++bool ++sepgsqlGetEnforce(void) ++{ ++ bool rc; ++ ++ if (local_enforce < 0) ++ { ++ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); ++ rc = selinux_state->enforcing; ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return rc; ++ } ++ ++ return (local_enforce > 0 ? true : false); ++} ++ ++int ++sepgsqlSetEnforce(int new_mode) ++{ ++ int old_mode = local_enforce; ++ ++ local_enforce = new_mode; ++ ++ return old_mode; ++} ++ ++/* ++ * sepgsqlAvcAudit ++ * ++ * It write out audit message, when auditdeny or auditallow ++ * matches the required permission bits. ++ * If external module support sepgsqlAvcAuditHook, it allows ++ * to write audit logs to external log manager, such as system ++ * auditd. ++ */ ++ ++PGDLLIMPORT sepgsqlAvcAuditHook_t sepgsqlAvcAuditHook = NULL; ++ ++static void ++sepgsqlAvcAudit(bool denied, char *scontext, char *tcontext, ++ uint16 tclass, uint32 audited, const char *audit_name) ++{ ++ StringInfoData buf; ++ uint32 mask; ++ const char *tclass_name; ++ ++ /* translate to human readable form */ ++ scontext = sepgsqlTransSecLabelOut(scontext); ++ tcontext = sepgsqlTransSecLabelOut(tcontext); ++ ++ /* permissions in text representation */ ++ initStringInfo(&buf); ++ appendStringInfo(&buf, "{"); ++ for (mask = 1; audited != 0; mask <<= 1) ++ { ++ if (audited & mask) ++ appendStringInfo(&buf, " %s", sepgsqlGetPermString(tclass, mask)); ++ ++ audited &= ~mask; ++ } ++ appendStringInfo(&buf, " }"); ++ ++ tclass_name = sepgsqlGetClassString(tclass); ++ ++ /* call external audit module, if loaded */ ++ if (sepgsqlAvcAuditHook) ++ (*sepgsqlAvcAuditHook) (denied, scontext, tcontext, ++ tclass_name, buf.data, audit_name); ++ else ++ { ++ appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, tclass_name); ++ if (audit_name) ++ appendStringInfo(&buf, " name=%s", audit_name); ++ ++ ereport(LOG, ++ (errcode(ERRCODE_SELINUX_AUDIT), ++ errmsg("SELinux: %s %s", ++ denied ? "denied" : "granted", buf.data))); ++ } ++} ++ ++/* ++ * sepgsqlAvcReclaim ++ * ++ * It wipes recently unused AVC entries, when the number of entries ++ * reaches AVC_HASH_NUM_NODES.. ++ */ ++static void ++sepgsqlAvcReclaim(avc_page *page) ++{ ++ ListCell *l; ++ avc_datum *cache; ++ ++ while (page->avc_count > AVC_HASH_NUM_NODES) ++ { ++ foreach (l, page->slot[page->lru_hint]) ++ { ++ cache = lfirst(l); ++ ++ if (cache->hot_cache) ++ cache->hot_cache = false; ++ else ++ { ++ list_delete_ptr(page->slot[page->lru_hint], cache); ++ pfree(cache); ++ page->avc_count--; ++ } ++ } ++ page->lru_hint = (page->lru_hint + 1) % AVC_HASH_NUM_SLOTS; ++ } ++} ++ ++/* ++ * sepgsqlAvcMakeEntry ++ * ++ * It makes a new AVC entry and insert it on the avc_page. ++ * If is hold more than AVC_HASH_NUM_NODES entries, recently unused ++ * avc_datum shall be reclaimed. ++ */ ++#define avc_hash_key(trelid,tsecid,tclass) \ ++ (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3))) ++ ++static avc_datum * ++sepgsqlAvcMakeEntry(avc_page *page, sepgsql_sid_t tsid, uint16 tclass) ++{ ++ security_context_t scontext, tcontext, ncontext; ++ security_class_t tclass_ex; ++ MemoryContext oldctx; ++ struct av_decision avd; ++ avc_datum *cache; ++ uint32 hash_key, index; ++ ++ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass); ++ index = hash_key % AVC_HASH_NUM_SLOTS; ++ ++ scontext = page->scontext; ++ tcontext = securityRawSecLabelOut(tsid.relid, tsid.secid); ++ ++ /* ++ * Compute SELinux permission ++ */ ++ tclass_ex = sepgsqlTransToExternalClass(tclass); ++ if (tclass_ex > 0) ++ { ++ if (security_compute_av_flags_raw(scontext, tcontext, ++ tclass_ex, 0, &avd) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: unable to compute av_decision: " ++ "scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, ++ sepgsqlGetClassString(tclass)))); ++ sepgsqlTransToInternalPerms(tclass, &avd); ++ } ++ else ++ { ++ /* fill it up as undefined class */ ++ avd.allowed = (security_deny_unknown() ? 0 : ~0UL); ++ avd.decided = ~0UL; ++ avd.auditallow = 0UL; ++ avd.auditdeny = ~0UL; ++ avd.flags = 0; ++ } ++ ++ /* ++ * Compute New security context ++ */ ++ if (security_compute_create_raw(scontext, tcontext, ++ tclass_ex, &ncontext) < 0) ++ { ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: unable to compute new context: " ++ "scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, sepgsqlGetClassString(tclass)))); ++ } ++ ++ /* ++ * Copy them to avc_datum ++ */ ++ oldctx = MemoryContextSwitchTo(AvcMemCtx); ++ PG_TRY(); ++ { ++ cache = palloc0(sizeof(avc_datum) + strlen(ncontext)); ++ } ++ PG_CATCH(); ++ { ++ freecon(ncontext); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ ++ cache->hash_key = hash_key; ++ cache->tclass = tclass; ++ cache->tsid.relid = tsid.relid; ++ cache->tsid.secid = tsid.secid; ++ /* cache->nsid shall be set later */ ++ ++ cache->allowed = avd.allowed; ++ cache->decided = avd.decided; ++ cache->auditallow = avd.auditallow; ++ cache->auditdeny = avd.auditdeny; ++ ++ cache->hot_cache = true; ++ if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) ++ cache->permissive = true; ++ strcpy(cache->ncontext, ncontext); ++ freecon(ncontext); ++ ++ sepgsqlAvcReclaim(page); ++ ++ page->slot[index] = lcons(cache, page->slot[index]); ++ page->avc_count++; ++ ++ MemoryContextSwitchTo(oldctx); ++ ++ return cache; ++} ++ ++/* ++ * sepgsqlAvcLookup ++ * ++ * It lookups required AVC entry. ++ */ ++static avc_datum * ++sepgsqlAvcLookup(avc_page *page, sepgsql_sid_t tsid, uint16 tclass) ++{ ++ avc_datum *cache = NULL; ++ uint32 hash_key, index; ++ ListCell *l; ++ ++ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass); ++ index = hash_key % AVC_HASH_NUM_SLOTS; ++ ++ foreach (l, page->slot[index]) ++ { ++ cache = lfirst(l); ++ if (cache->hash_key == hash_key ++ && cache->tclass == tclass ++ && cache->tsid.relid == tsid.relid ++ && cache->tsid.secid == tsid.secid) ++ { ++ cache->hot_cache = true; ++ return cache; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * sepgsqlAvcSwitchClientLabel() ++ * ++ * It switches the current avc_page. ++ * An avc_page is a set of cached access control decisions associated ++ * with a certain privilege of the client. This structure enables to ++ * lookup required avc_datum without any comparison to the subject ++ * label. ++ */ ++void ++sepgsqlAvcSwitchClient(const char *scontext) ++{ ++ MemoryContext oldctx; ++ avc_page *new_page; ++ int i; ++ ++ if (current_page) ++ { ++ new_page = current_page; ++ do { ++ if (strcmp(new_page->scontext, scontext) == 0) ++ { ++ current_page = new_page; ++ return; ++ } ++ new_page = new_page->next; ++ } while (new_page != current_page); ++ } ++ ++ /* Not found, create a new avc_page */ ++ oldctx = MemoryContextSwitchTo(AvcMemCtx); ++ new_page = palloc0(sizeof(avc_page)); ++ new_page->scontext = pstrdup(scontext); ++ MemoryContextSwitchTo(oldctx); ++ ++ for (i=0; i < AVC_HASH_NUM_SLOTS; i++) ++ new_page->slot[i] = NIL; ++ ++ if (!current_page) ++ new_page->next = new_page; ++ else ++ { ++ new_page->next = current_page->next; ++ current_page->next = new_page; ++ } ++ ++ current_page = new_page; ++} ++ ++/* ++ * sepgsqlClientHasPerms ++ * ++ * It checks client's privileges on the given object using avc. ++ */ ++bool ++sepgsqlClientHasPerms(sepgsql_sid_t tsid, ++ uint16 tclass, uint32 required, ++ const char *audit_name, bool abort) ++{ ++ avc_datum *cache; ++ uint32 denied, audited; ++ bool result = true; ++ ++ Assert(required != 0); ++ ++ do { ++ cache = sepgsqlAvcLookup(current_page, tsid, tclass); ++ if (!cache) ++ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass); ++ } while (!sepgsqlAvcCheckValid()); ++ ++ denied = required & ~cache->allowed; ++ audited = denied ? (denied & cache->auditdeny) ++ : (required & cache->auditallow); ++ if (audited) ++ { ++ sepgsqlAvcAudit(!!denied, ++ current_page->scontext, ++ securityRawSecLabelOut(tsid.relid, tsid.secid), ++ cache->tclass, audited, audit_name); ++ } ++ ++ if (denied) ++ { ++ if (!sepgsqlGetEnforce() || cache->permissive) ++ cache->allowed |= required; /* prevent flood of audit log */ ++ else ++ { ++ if (abort) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: security policy violation"))); ++ result = false; ++ } ++ } ++ ++ return result; ++} ++ ++/* ++ * sepgsqlClientCreateSecid ++ * sepgsqlClientCreateLabel ++ */ ++sepgsql_sid_t ++sepgsqlClientCreateSecid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) ++{ ++ sepgsql_sid_t nsid; ++ avc_datum *cache; ++ int index; ++ ++ do { ++ cache = sepgsqlAvcLookup(current_page, tsid, tclass); ++ if (!cache) ++ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass); ++ ++ index = (nrelid % AVC_DATUM_NSID_SLOTS); ++ if (cache->nsid[index].relid != nrelid) ++ { ++ cache->nsid[index].secid ++ = securityRawSecLabelIn(nrelid, cache->ncontext); ++ cache->nsid[index].relid = nrelid; ++ } ++ nsid = cache->nsid[index]; ++ } while (!sepgsqlAvcCheckValid()); ++ ++ return nsid; ++} ++ ++security_context_t ++sepgsqlClientCreateLabel(sepgsql_sid_t tsid, uint16 tclass) ++{ ++ avc_datum *cache; ++ ++ do { ++ cache = sepgsqlAvcLookup(current_page, tsid, tclass); ++ if (!cache) ++ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass); ++ } while (!sepgsqlAvcCheckValid()); ++ ++ return cache->ncontext; ++} ++ ++/* ++ * sepgsqlComputePerms ++ * sepgsqlComputeCreate ++ * ++ * The following two functions make a query to in-kernel SELinux ++ * without userspace caches, due to some reasons. ++ * The AVC can cover most of cases, but some of corner cases are ++ * not suitable for AVC structure, so we need uncached interfaces. ++ * For example, AVC is unavailable when we tries to load a shared ++ * library module, because security context of the library does not ++ * have its security identifier, so we cannot put it on AVC. ++ */ ++bool ++sepgsqlComputePerms(char *scontext, char *tcontext, ++ uint16 tclass_in, uint32 required, ++ const char *audit_name, bool abort) ++{ ++ access_vector_t denied, audited; ++ security_class_t tclass_ex; ++ struct av_decision avd; ++ ++ Assert(required != 0); ++ ++ tclass_ex = sepgsqlTransToExternalClass(tclass_in); ++ if (tclass_ex > 0) ++ { ++ /* ++ * security_compute_av_flags_raw() is a SELinux's API that ++ * returns its access control decision based on the security ++ * policy, to the given combination of user's privilege ++ * (scontext; security label of the client process), ++ * target's attribute (tcontext; security label of the ++ * object) and type of actions (tclass; object classes). ++ * ++ * The returned avd.allowed is a bitmap of allowed actions. ++ */ ++ if (security_compute_av_flags_raw(scontext, tcontext, ++ tclass_ex, 0, &avd) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not compute av_decision: " ++ "scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, ++ sepgsqlGetClassString(tclass_in)))); ++ sepgsqlTransToInternalPerms(tclass_in, &avd); ++ } ++ else ++ { ++ /* ++ * If security policy does not support database related ++ * permissions, it fulls up permission bits by dummy ++ * data. ++ * If security_deny_unknown() returns positive value, ++ * undefined permissions should not be allowed. ++ * Otherwise, it shall be allowed. ++ */ ++ avd.allowed = (security_deny_unknown() > 0 ? 0 : ~0UL); ++ avd.decided = ~0UL; ++ avd.auditallow = 0UL; ++ avd.auditdeny = ~0UL; ++ avd.flags = 0; ++ } ++ ++ denied = required & ~avd.allowed; ++ audited = denied ? (denied & avd.auditdeny) ++ : (required & avd.auditallow); ++ if (audited) ++ { ++ /* ++ * If security policy requires to generate an audit log ++ * record for the given request, it should be logged. ++ */ ++ sepgsqlAvcAudit(!!denied, scontext, tcontext, ++ tclass_in, audited, audit_name); ++ } ++ ++ /* ++ * If any required permissions are not allowed, and ++ * SE-PgSQL performs in enforcing mode, and the given ++ * combination of subject, object and action does not ++ * have special flag to be handled as permission, ++ * SE-PgSQL returns false or raises an error. ++ * Otherwise, it returns true that means required ++ * actions are allowed. ++ */ ++ if (!denied || /* no policy violation */ ++ !sepgsqlGetEnforce() || /* permissive mode */ ++ (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) != 0) /* permissive domain */ ++ return true; ++ ++ if (abort) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: security policy violation"))); ++ ++ return false; ++} ++ ++char * ++sepgsqlComputeCreate(char *scontext, char *tcontext, uint16 tclass_in) ++{ ++ security_context_t ncontext, result; ++ security_class_t tclass_ex; ++ ++ tclass_ex = sepgsqlTransToExternalClass(tclass_in); ++ /* ++ * security_compute_create_raw() is a SELinux's API that ++ * returns a default security context to be assigned on ++ * a new object (categorized by object class) when a client ++ * labeled as scontext tries to create a new one under the ++ * parent object labeled as tcontext. ++ */ ++ if (security_compute_create_raw(scontext, tcontext, ++ tclass_ex, &ncontext) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("SELinux: could not compute a new context " ++ "scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, sepgsqlGetClassString(tclass_in)))); ++ PG_TRY(); ++ { ++ result = pstrdup(ncontext); ++ } ++ PG_CATCH(); ++ { ++ freecon(ncontext); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(ncontext); ++ ++ return result; ++} ++ ++/* ++ * SELinux state monitoring process ++ * ++ * This process is forked from postmaster to monitor the state of SELinux. ++ * SELinux can make a notifier message to userspace object manager via ++ * netlink socket. When it receives the message, it updates selinux_state ++ * structure assigned on shared memory region to make any instance reset ++ * its AVC soon. ++ */ ++static int ++sepgsql_cb_log(int type, const char *fmt, ...) ++{ ++ char *c, buffer[1024]; ++ va_list ap; ++ ++ va_start(ap, fmt); ++ vsnprintf(buffer, sizeof(buffer), fmt, ap); ++ va_end(ap); ++ ++ c = strrchr(buffer, '\n'); ++ if (c) ++ *c = '\0'; ++ ++ ereport(LOG, ++ (errcode(ERRCODE_SELINUX_INFO), ++ errmsg("%s", buffer))); ++ ++ return 0; ++} ++ ++static int ++sepgsql_cb_setenforce(int enforce) ++{ ++ /* switch enforcing/permissive */ ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ selinux_state->enforcing = (enforce ? true : false); ++ selinux_state->version++; ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return 0; ++} ++ ++static int ++sepgsql_cb_policyload(int seqno) ++{ ++ /* invalidate local avc */ ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ selinux_state->version++; ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return 0; ++} ++ ++void ++sepgsqlReceiverMain(void) ++{ ++ union selinux_callback cb; ++ ++ Assert(sepgsqlIsEnabled()); ++ ++#ifdef HAVE_SETSID ++ if (setsid() < 0) ++ elog(FATAL, "setsid() failed: %m"); ++#endif ++ ++ /* ++ * setup the signal handler ++ */ ++ pqinitmask(); ++ pqsignal(SIGHUP, SIG_IGN); ++ pqsignal(SIGINT, SIG_IGN); ++ pqsignal(SIGTERM, exit); ++ pqsignal(SIGQUIT, exit); ++ pqsignal(SIGUSR1, SIG_IGN); ++ pqsignal(SIGUSR2, SIG_IGN); ++ pqsignal(SIGCHLD, SIG_DFL); ++ PG_SETMASK(&UnBlockSig); ++ ++ /* ++ * map shared memory segment ++ */ ++ sepgsqlShmemInit(); ++ ++ ereport(LOG, ++ (errcode(ERRCODE_SELINUX_INFO), ++ errmsg("SELinux: security policy monitor (pid=%u)", getpid()))); ++ /* ++ * setup callback functions from avc_netlink_loop() ++ */ ++ cb.func_log = sepgsql_cb_log; ++ selinux_set_callback(SELINUX_CB_LOG, cb); ++ cb.func_setenforce = sepgsql_cb_setenforce; ++ selinux_set_callback(SELINUX_CB_SETENFORCE, cb); ++ cb.func_policyload = sepgsql_cb_policyload; ++ selinux_set_callback(SELINUX_CB_POLICYLOAD, cb); ++ ++ /* ++ * open netlink socket and wait for messages ++ */ ++ avc_netlink_open(1); ++ ++ avc_netlink_loop(); ++ ++ exit(0); ++} +diff --git a/src/backend/security/sepgsql/bridge.c b/src/backend/security/sepgsql/bridge.c +new file mode 100644 +index 0000000..3a3630c +--- /dev/null ++++ b/src/backend/security/sepgsql/bridge.c +@@ -0,0 +1,2920 @@ ++/* ++ * src/backend/security/sepgsql/bridge.c ++ * ++ * New style security hooks for SE-PostgreSQL ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++ ++#include "access/sysattr.h" ++#include "catalog/heap.h" ++#include "catalog/indexing.h" ++#include "catalog/pg_authid.h" ++#include "catalog/pg_cast.h" ++#include "catalog/pg_conversion.h" ++#include "catalog/pg_database.h" ++#include "catalog/pg_foreign_data_wrapper.h" ++#include "catalog/pg_foreign_server.h" ++#include "catalog/pg_language.h" ++#include "catalog/pg_largeobject_metadata.h" ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_operator.h" ++#include "catalog/pg_opclass.h" ++#include "catalog/pg_opfamily.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_rewrite.h" ++#include "catalog/pg_security.h" ++#include "catalog/pg_tablespace.h" ++#include "catalog/pg_ts_parser.h" ++#include "catalog/pg_ts_dict.h" ++#include "catalog/pg_ts_template.h" ++#include "catalog/pg_ts_config.h" ++#include "catalog/pg_type.h" ++#include "catalog/pg_user_mapping.h" ++#include "commands/dbcommands.h" ++#include "miscadmin.h" ++#include "security/sepgsql.h" ++#include "utils/builtins.h" ++#include "utils/fmgroids.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++ ++#include ++#include ++ ++/* ------------------------------------------------------------ * ++ * Common Helper Routines ++ * ------------------------------------------------------------ */ ++static bool sepgsql_database_common(Oid datOid, uint32 required, bool abort); ++static bool sepgsql_schema_common(Oid nspOid, uint32 required, bool abort); ++static bool sepgsql_attribute_common(Oid relOid, AttrNumber attnum, ++ uint32 required, bool abort); ++static bool sepgsql_relation_common(Oid relOid, uint32 required, bool abort); ++static bool sepgsql_proc_common(Oid procOid, uint32 required, bool abort); ++static bool sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort); ++static bool sepgsql_foreign_server_common(Oid fsrvOid, uint32 required, bool abort); ++static bool sepgsql_language_common(Oid langOid, uint32 required, bool abort); ++static bool sepgsql_operator_common(Oid oprOid, uint32 required, bool abort); ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_database related security hooks ++ * ++ * ------------------------------------------------------------ */ ++static bool ++sepgsql_database_common(Oid datOid, uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ const char *auname; ++ bool rc; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(datOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for database: %u", datOid); ++ ++ auname = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); ++ ++ sid = sepgsqlGetTupleSecid(DatabaseRelationId, tuple, &tclass); ++ ++ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); ++ ++ ReleaseSysCache(tuple); ++ ++ return rc; ++} ++ ++Oid ++sepgsql_database_create(const char *datName, Oid srcDatOid, DefElem *newLabel) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ if (!newLabel) ++ sid = sepgsqlGetDefaultDatabaseSecid(srcDatOid); ++ else ++ { ++ sid.relid = DatabaseRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, ++ strVal(newLabel->arg)); ++ } ++ ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_DATABASE, ++ SEPG_DB_DATABASE__CREATE, ++ datName, true); ++ return sid.secid; ++} ++ ++void ++sepgsql_database_alter(Oid datOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_database_common(datOid, SEPG_DB_DATABASE__SETATTR, true); ++} ++ ++void ++sepgsql_database_drop(Oid datOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_database_common(datOid, SEPG_DB_DATABASE__DROP, true); ++} ++ ++Oid ++sepgsql_database_relabel(Oid datOid, DefElem *newLabel) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ { ++ if (newLabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SELinux is disabled now"))); ++ ++ return InvalidOid; ++ } ++ sid.relid = DatabaseRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ ++ /* db_database:{setattr relabelfrom} to older seclabel */ ++ sepgsql_database_common(datOid, ++ SEPG_DB_DATABASE__SETATTR | ++ SEPG_DB_DATABASE__RELABELFROM, true); ++ ++ /* db_database:{relabelto} to newer seclabel */ ++ sepgsqlClientHasPerms(sid, ++ SEPG_CLASS_DB_DATABASE, ++ SEPG_DB_DATABASE__RELABELTO, ++ get_database_name(datOid), true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_database_grant(Oid datOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_database_common(datOid, SEPG_DB_DATABASE__SETATTR, true); ++} ++ ++void ++sepgsql_database_access(Oid datOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_database_common(datOid, SEPG_DB_DATABASE__ACCESS, true); ++} ++ ++void ++sepgsql_database_load_module(Oid datOid, const char *filename) ++{ ++ HeapTuple tuple; ++ security_context_t filecon; ++ security_context_t datcon; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ /* ++ * It assumes preloaded libraries are secure, ++ * because it can be set up using guc variable ++ * not any SQL statements. ++ */ ++ if (GetProcessingMode() == InitProcessing) ++ return; ++ ++ /* Get database context */ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(datOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for database: %u", datOid); ++ ++ datcon = securityRawSecLabelOut(DatabaseRelationId, ++ HeapTupleGetSecid(tuple)); ++ ReleaseSysCache(tuple); ++ ++ /* Get library context */ ++ if (getfilecon_raw(filename, &filecon) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not access file \"%s\": %m", filename))); ++ PG_TRY(); ++ { ++ sepgsqlComputePerms(datcon, ++ filecon, ++ SEPG_CLASS_DB_DATABASE, ++ SEPG_DB_DATABASE__LOAD_MODULE, ++ filename, true); ++ } ++ PG_CATCH(); ++ { ++ freecon(filecon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(filecon); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_namespace related security hooks ++ * ++ * ------------------------------------------------------------ */ ++static bool ++sepgsql_schema_common(Oid nspOid, uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ const char *auname; ++ bool rc; ++ ++ tuple = SearchSysCache(NAMESPACEOID, ++ ObjectIdGetDatum(nspOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for namespace: %u", nspOid); ++ ++ sid = sepgsqlGetTupleSecid(NamespaceRelationId, tuple, &tclass); ++ ++ auname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); ++ ++ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); ++ ++ ReleaseSysCache(tuple); ++ ++ return rc; ++} ++ ++Oid ++sepgsql_schema_create(const char *nspName, bool isTemp, DefElem *newLabel) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ if (!newLabel) ++ sid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); ++ else ++ { ++ sid.relid = NamespaceRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ } ++ ++ sepgsqlClientHasPerms(sid, ++ SEPG_CLASS_DB_SCHEMA, ++ SEPG_DB_SCHEMA__CREATE, ++ nspName, true); ++ return sid.secid; ++} ++ ++void ++sepgsql_schema_alter(Oid nspOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SETATTR, true); ++} ++ ++void ++sepgsql_schema_drop(Oid nspOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__DROP, true); ++} ++ ++Oid ++sepgsql_schema_relabel(Oid nspOid, DefElem *newLabel) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ { ++ if (newLabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SELinux is disabled now"))); ++ return InvalidOid; ++ } ++ sid.relid = NamespaceRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ ++ /* db_schema:{setattr relabelfrom} for older seclabel */ ++ sepgsql_schema_common(nspOid, ++ SEPG_DB_SCHEMA__SETATTR | ++ SEPG_DB_SCHEMA__RELABELFROM, true); ++ ++ /* db_schema:{relabelto} for newer seclabel */ ++ sepgsqlClientHasPerms(sid, ++ SEPG_CLASS_DB_SCHEMA, ++ SEPG_DB_SCHEMA__RELABELTO, ++ get_namespace_name(nspOid), true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_schema_grant(Oid nspOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SETATTR, true); ++} ++ ++bool ++sepgsql_schema_search(Oid nspOid, bool abort) ++{ ++ if (!sepgsqlIsEnabled()) ++ return true; ++ ++ return sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SEARCH, abort); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_attribute related security hooks ++ * ++ * ------------------------------------------------------------ */ ++static bool ++sepgsql_attribute_common(Oid relOid, AttrNumber attnum, ++ uint32 required, bool abort) ++{ ++ Form_pg_attribute attForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ char auname[NAMEDATALEN * 2 + 3]; ++ bool rc = true; ++ ++ /* Caller prevent case when relkind != RELKIND_RELATION */ ++ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); ++ ++ tuple = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(relOid), ++ Int16GetDatum(attnum), ++ 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for attribute %d of relation %u", ++ attnum, relOid); ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ ++ /* ++ * NOTE: when a table to be dropped, corresponding attributes ++ * are also removed. Some of them can be already logically ++ * dropped using ALTER TABLE ... DROP statement. ++ * In this case, SE-PostgreSQL does not check anything. ++ * If any other situation touches dropped column, it is a bug. ++ */ ++ if (attForm->attisdropped) ++ goto skip; ++ ++ sprintf(auname, "%s.%s", get_rel_name(relOid), NameStr(attForm->attname)); ++ ++ sid = sepgsqlGetTupleSecid(AttributeRelationId, tuple, &tclass); ++ ++ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); ++ ++skip: ++ ReleaseSysCache(tuple); ++ ++ return rc; ++} ++ ++Oid ++sepgsql_attribute_create(Oid relOid, ColumnDef *cdef) ++{ ++ sepgsql_sid_t sid; ++ char relkind; ++ ++ if (!sepgsqlIsEnabled()) ++ { ++ if (cdef->secLabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SELinux is disabled now"))); ++ return InvalidOid; ++ } ++ ++ relkind = get_rel_relkind(relOid); ++ if (relkind == RELKIND_RELATION) ++ { ++ char auname[NAMEDATALEN * 2 + 3]; ++ ++ if (!cdef->secLabel) ++ sid = sepgsqlGetDefaultColumnSecid(relOid); ++ else ++ { ++ char *label = strVal(((DefElem *)cdef->secLabel)->arg); ++ ++ sid.relid = AttributeRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, label); ++ } ++ ++ sprintf(auname, "%s.%s", get_rel_name(relOid), cdef->colname); ++ sepgsqlClientHasPerms(sid, ++ SEPG_CLASS_DB_COLUMN, ++ SEPG_DB_COLUMN__CREATE, ++ auname, true); ++ } ++ else ++ { ++ /* no need to check for toast relation */ ++ if (relkind != RELKIND_TOASTVALUE) ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ return InvalidOid; ++ } ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_attribute_alter(Oid relOid, const char *attname) ++{ ++ AttrNumber attno; ++ char relkind; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* ++ * If the target attribute does not exist, an error ++ * shall be raised later. ++ */ ++ attno = get_attnum(relOid, attname); ++ if (attno == InvalidAttrNumber) ++ return; ++ ++ relkind = get_rel_relkind(relOid); ++ if (relkind == RELKIND_RELATION) ++ { ++ sepgsql_attribute_common(relOid, attno, SEPG_DB_COLUMN__SETATTR, true); ++ } ++ else if (relkind != RELKIND_TOASTVALUE) ++ { ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ } ++} ++ ++void ++sepgsql_attribute_drop(Oid relOid, AttrNumber attnum) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* ++ * We only need to check db_column:{drop} when relkind equals ++ * RELKIND_RELATION, because db_xxx:{drop} permission is already ++ * checked in other cases. (e.g DROP SEQUENCE, ...) ++ */ ++ if (get_rel_relkind(relOid) == RELKIND_RELATION) ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__DROP, true); ++} ++ ++void ++sepgsql_attribute_grant(Oid relOid, AttrNumber attnum) ++{ ++ char relkind; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ relkind = get_rel_relkind(relOid); ++ if (relkind == RELKIND_RELATION) ++ { ++ sepgsql_attribute_common(relOid, attnum, SEPG_DB_COLUMN__SETATTR, true); ++ } ++ else if (relkind != RELKIND_TOASTVALUE) ++ { ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ } ++} ++ ++Oid ++sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, DefElem *newLabel) ++{ ++ sepgsql_sid_t sid; ++ char auname[NAMEDATALEN * 2 + 3]; ++ ++ if (!sepgsqlIsEnabled()) ++ { ++ if (!newLabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SELinux is disabled now"))); ++ return InvalidOid; ++ } ++ ++ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); ++ ++ sid.relid = AttributeRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ ++ /* db_column:{setattr relabelfrom} */ ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__SETATTR | ++ SEPG_DB_COLUMN__RELABELFROM, true); ++ ++ /* db_column:{relabelto} */ ++ sprintf(auname, "%s.%s", ++ get_rel_name(relOid), ++ get_attname(relOid, attnum)); ++ sepgsqlClientHasPerms(sid, ++ SEPG_CLASS_DB_COLUMN, ++ SEPG_DB_COLUMN__RELABELTO, ++ auname, true); ++ ++ return sid.secid; ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_class related security hooks ++ * ++ * ------------------------------------------------------------ */ ++static bool ++sepgsql_relation_common(Oid relOid, uint32 required, bool abort) ++{ ++ Form_pg_class relForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ bool rc; ++ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relOid); ++ relForm = (Form_pg_class) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(RelationRelationId, tuple, &tclass); ++ rc = sepgsqlClientHasPerms(sid, tclass, required, ++ NameStr(relForm->relname), abort); ++ ++ ReleaseSysCache(tuple); ++ ++ return rc; ++} ++ ++/* ++ * sepgsql_relation_create ++ * It returns an array of security identifier for the new table ++ * and columns to be assigned. The corresponding security labels ++ * are already checked for db_table/db_sequence/db_column:{create} ++ * permission. ++ * In the default labeling rule, a column inherits the security ++ * label of its table, but we cannot refer it using system caches, ++ * because the command counter is not incremented under the ++ * heap_create_with_catalog(). Thus, we need to compute and check ++ * them prior to the actual creation of table and columns. ++ */ ++Oid * ++sepgsql_relation_create(const char *relName, ++ char relkind, ++ TupleDesc tupDesc, ++ Oid nspOid, ++ DefElem *relLabel, ++ List *colList, ++ bool createAs, ++ bool permission) ++{ ++ Oid *secLabels; ++ sepgsql_sid_t relsid; ++ uint16 tclass; ++ uint32 required; ++ int index; ++ ++ if (!sepgsqlIsEnabled()) ++ return NULL; ++ ++ switch (relkind) ++ { ++ case RELKIND_RELATION: ++ if (!relLabel) ++ relsid = sepgsqlGetDefaultTableSecid(nspOid); ++ else ++ { ++ relsid.relid = RelationRelationId; ++ relsid.secid = securityTransSecLabelIn(relsid.relid, ++ strVal(relLabel->arg)); ++ } ++ tclass = SEPG_CLASS_DB_TABLE; ++ required = SEPG_DB_TABLE__CREATE; ++ if (createAs) ++ required |= SEPG_DB_TABLE__INSERT; ++ break; ++ ++ case RELKIND_SEQUENCE: ++ if (!relLabel) ++ relsid = sepgsqlGetDefaultSequenceSecid(nspOid); ++ else ++ { ++ relsid.relid = RelationRelationId; ++ relsid.secid = securityTransSecLabelIn(relsid.relid, ++ strVal(relLabel->arg)); ++ } ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ required = SEPG_DB_SEQUENCE__CREATE; ++ break; ++ ++ default: ++ if (!relLabel) ++ relsid = sepgsqlGetDefaultTupleSecid(RelationRelationId); ++ else ++ { ++ /* should not be happen */ ++ relsid.relid = RelationRelationId; ++ relsid.secid = securityTransSecLabelIn(relsid.relid, ++ strVal(relLabel->arg)); ++ } ++ tclass = SEPG_CLASS_DB_TUPLE; ++ required = SEPG_DB_TUPLE__INSERT; ++ break; ++ } ++ ++ /* ++ * The secLabeld array stores security identifiers to be assigned ++ * on the new table and columns. ++ * ++ * secLabels[0] is security identifier of the table. ++ * secLabels[attnum - FirstLowInvalidHeapAttributeNumber] ++ * is security identifier of columns (if necessary). ++ */ ++ secLabels = palloc0(sizeof(Oid) * (tupDesc->natts ++ - FirstLowInvalidHeapAttributeNumber)); ++ ++ /* relation's security identifier to be assigned on */ ++ secLabels[0] = relsid.secid; ++ ++ /* ++ * Note that this hook can be called during initdb processes. ++ * It is an exception of access controls, so we skip any checks. ++ * ++ * And, we don't need any checks for toast relations, because ++ * it is a quite internal stuff. ++ */ ++ if (permission) ++ { ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_table:{create}, db_sequence:{create} or db_tuple:{insert} */ ++ sepgsqlClientHasPerms(relsid, tclass, required, relName, true); ++ } ++ ++ /* no individual security context expect for RELKIND_RELATION */ ++ if (relkind != RELKIND_RELATION) ++ return secLabels; ++ ++ /* ++ * db_column:{create} permission ++ */ ++ for (index = FirstLowInvalidHeapAttributeNumber + 1; ++ index < tupDesc->natts; ++ index++) ++ { ++ Form_pg_attribute attr; ++ sepgsql_sid_t attsid = { InvalidOid, InvalidOid }; ++ char attname[NAMEDATALEN * 2 + 3]; ++ ListCell *l; ++ ++ /* skip unnecessary attributes */ ++ if (index == ObjectIdAttributeNumber && !tupDesc->tdhasoid) ++ continue; ++ ++ if (index < 0) ++ attr = SystemAttributeDefinition(index, tupDesc->tdhasoid); ++ else ++ attr = tupDesc->attrs[index]; ++ ++ /* Is there any given security context? */ ++ foreach (l, colList) ++ { ++ ColumnDef *cdef = lfirst(l); ++ ++ if (cdef->secLabel && ++ strcmp(cdef->colname, NameStr(attr->attname)) == 0) ++ { ++ attsid.relid = AttributeRelationId; ++ attsid.secid = securityTransSecLabelIn(attsid.relid, ++ strVal(((DefElem *)cdef->secLabel)->arg)); ++ break; ++ } ++ } ++ ++ /* default security context, if not given */ ++ if (!SidIsValid(attsid)) ++ attsid = sepgsqlClientCreateSecid(relsid, ++ SEPG_CLASS_DB_COLUMN, ++ AttributeRelationId); ++ if (permission) ++ { ++ required = SEPG_DB_COLUMN__CREATE; ++ ++ if (createAs) ++ required |= SEPG_DB_COLUMN__INSERT; ++ ++ /* db_column:{create (insert)} */ ++ sprintf(attname, "%s.%s", relName, NameStr(attr->attname)); ++ sepgsqlClientHasPerms(attsid, ++ SEPG_CLASS_DB_COLUMN, ++ required, attname, true); ++ } ++ /* column's security identifier to be assigend on */ ++ secLabels[index - FirstLowInvalidHeapAttributeNumber] = attsid.secid; ++ } ++ ++ return secLabels; ++} ++ ++/* ++ * sepgsql_relation_copy ++ * It returns an array of security identifier of table and columns ++ * to be copied on make_new_heap(). It actually create a new temporary ++ * relation and insert all the tuples within original one into the ++ * temporary one, but swap_relation_files() swaps their file nodes. ++ * Thus, there are no changes from the viewpoint of users. ++ * SE-PostgreSQL also does not check and change anything. It simply ++ * copies security identifier of the source relation to the destination ++ * relation. ++ */ ++Oid * ++sepgsql_relation_copy(Relation src) ++{ ++ Oid *secLabels; ++ HeapTuple tuple; ++ Oid relOid = RelationGetRelid(src); ++ int index; ++ ++ if (!sepgsqlIsEnabled()) ++ return NULL; ++ ++ /* see the comment at sepgsqlCreateTableColumn*/ ++ secLabels = palloc0(sizeof(Oid) * (RelationGetDescr(src)->natts ++ - FirstLowInvalidHeapAttributeNumber)); ++ ++ /* copy table's security identifier */ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation \"%s\"", ++ RelationGetRelationName(src)); ++ ++ secLabels[0] = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ ++ /* copy column's security identifier */ ++ for (index = FirstLowInvalidHeapAttributeNumber + 1; ++ index < RelationGetDescr(src)->natts; ++ index++) ++ { ++ Form_pg_attribute attr; ++ ++ if (index < 0) ++ attr = SystemAttributeDefinition(index, true); ++ else ++ attr = RelationGetDescr(src)->attrs[index]; ++ ++ tuple = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(relOid), ++ Int16GetDatum(attr->attnum), ++ 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ continue; ++ ++ secLabels[index - FirstLowInvalidHeapAttributeNumber] ++ = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ return secLabels; ++} ++ ++void ++sepgsql_relation_alter(Oid relOid, const char *newName, Oid newNsp) ++{ ++ Form_pg_class relForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relOid); ++ relForm = (Form_pg_class) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(RelationRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TABLE__SETATTR, ++ NameStr(relForm->relname), true); ++ ++ /* db_schema:{add_name remove_name}, if necessary */ ++ if (newName || OidIsValid(newNsp)) ++ { ++ if (!OidIsValid(newNsp)) ++ sepgsql_schema_common(relForm->relnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ else ++ { ++ sepgsql_schema_common(relForm->relnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); ++ } ++ } ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsql_relation_drop(Oid relOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ if (get_rel_relkind(relOid) == RELKIND_TOASTVALUE) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__DROP, true); ++} ++ ++void ++sepgsql_relation_grant(Oid relOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++} ++ ++Oid ++sepgsql_relation_relabel(Oid relOid, DefElem *newLabel) ++{ ++ sepgsql_sid_t sid; ++ char relkind; ++ ++ if (!sepgsqlIsEnabled()) ++ { ++ if (newLabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SELinux is disabled now"))); ++ return InvalidOid; ++ } ++ ++ relkind = get_rel_relkind(relOid); ++ if (relkind != RELKIND_RELATION && relkind != RELKIND_SEQUENCE) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set security label on \"%s\"", ++ get_rel_name(relOid)))); ++ ++ /* input security context */ ++ sid.relid = RelationRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ ++ /* db_table/db_sequence:{setattr relabelfrom} */ ++ sepgsql_relation_common(relOid, ++ SEPG_DB_TABLE__SETATTR | ++ SEPG_DB_TABLE__RELABELFROM, true); ++ ++ /* db_table/db_sequence:{relabelto} */ ++ sepgsqlClientHasPerms(sid, ++ (relkind == RELKIND_RELATION ++ ? SEPG_CLASS_DB_TABLE ++ : SEPG_CLASS_DB_SEQUENCE), ++ SEPG_DB_TABLE__RELABELTO, ++ get_rel_name(relOid), true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_relation_get_transaction_id(Oid relOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__GETATTR, true); ++} ++ ++void ++sepgsql_relation_copy_definition(Oid relOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__GETATTR, true); ++} ++ ++void ++sepgsql_relation_truncate(Relation rel) ++{ ++ HeapScanDesc scan; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* check db_table:{delete} permission */ ++ sepgsql_relation_common(RelationGetRelid(rel), ++ SEPG_DB_TABLE__DELETE, true); ++ ++ /* row-level access control is enabled? */ ++ if (!sepostgresql_row_level) ++ return; ++ ++ /* check db_tuple:{delete} permission */ ++ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); ++ ++ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) ++ { ++ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NULL, true); ++ } ++ heap_endscan(scan); ++} ++ ++void ++sepgsql_relation_lock(Oid relOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ if (get_rel_relkind(relOid) != RELKIND_RELATION) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__LOCK, true); ++} ++ ++void ++sepgsql_view_replace(Oid viewOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ Assert(get_rel_relkind(viewOid) == RELKIND_VIEW); ++ ++ sepgsql_relation_common(viewOid, SEPG_DB_TABLE__SETATTR, true); ++} ++ ++void ++sepgsql_index_create(Oid relOid, Oid nspOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* db_table:{setattr} */ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++} ++ ++void ++sepgsql_sequence_get_value(Oid seqOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); ++ ++ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__GET_VALUE, true); ++} ++ ++void ++sepgsql_sequence_next_value(Oid seqOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); ++ ++ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__NEXT_VALUE, true); ++} ++ ++void ++sepgsql_sequence_set_value(Oid seqOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); ++ ++ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__SET_VALUE, true); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_proc related security hooks ++ * ++ * ------------------------------------------------------------ */ ++static bool ++sepgsql_proc_common(Oid procOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t sid; ++ HeapTuple tuple; ++ uint16 tclass; ++ const char *auname; ++ bool rc; ++ ++ tuple = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(procOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for procedure: %u", procOid); ++ ++ auname = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); ++ sid = sepgsqlGetTupleSecid(ProcedureRelationId, tuple, &tclass); ++ ++ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); ++ ++ ReleaseSysCache(tuple); ++ ++ return rc; ++} ++ ++Oid ++sepgsql_proc_create(const char *procName, HeapTuple oldTup, ++ Oid nspOid, Oid langOid, DefElem *newLabel) ++{ ++ sepgsql_sid_t sid; ++ //HeapTuple tuple; ++ uint32 required; ++ //bool trusted; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ if (!HeapTupleIsValid(oldTup)) ++ { ++ /* create a new function */ ++ required = SEPG_DB_PROCEDURE__CREATE; ++ if (!newLabel) ++ sid = sepgsqlGetDefaultProcedureSecid(nspOid); ++ else ++ { ++ sid.relid = ProcedureRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ } ++ } ++ else if (!newLabel) ++ { ++ /* replace an existing function, without any label */ ++ required = SEPG_DB_PROCEDURE__SETATTR; ++ sid = sepgsqlGetTupleSecid(ProcedureRelationId, oldTup, NULL); ++ } ++ else ++ { ++ /* replace an existing function, with relabeling */ ++ sepgsql_proc_common(HeapTupleGetOid(oldTup), ++ SEPG_DB_PROCEDURE__SETATTR | ++ SEPG_DB_PROCEDURE__RELABELFROM, true); ++ ++ required = SEPG_DB_PROCEDURE__RELABELTO; ++ sid = sepgsqlGetTupleSecid(ProcedureRelationId, oldTup, NULL); ++ } ++ ++#if 0 ++ /* Procedural language is trusted? */ ++ tuple = SearchSysCache(LANGOID, ++ ObjectIdGetDatum(langOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for procedural langugage: %u", langOid); ++ ++ trusted = ((Form_pg_language) GETSTRUCT(tuple))->lanpltrusted; ++ if (!trusted) ++ required |= SEPG_DB_PROCEDURE__UNTRUSTED; ++ ++ ReleaseSysCache(tuple); ++#endif ++ ++ /* check it */ ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_PROCEDURE, ++ required, procName, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_proc_alter(Oid procOid, const char *newName, Oid newNsp) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ if (newName || OidIsValid(newNsp)) ++ { ++ HeapTuple tuple; ++ Oid oldNsp; ++ ++ tuple = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(procOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for function %u", procOid); ++ ++ oldNsp = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; ++ ++ ReleaseSysCache(tuple); ++ ++ if (!OidIsValid(newNsp)) ++ { ++ sepgsql_schema_common(oldNsp, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ else ++ { ++ sepgsql_schema_common(oldNsp, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); ++ } ++ } ++} ++ ++void ++sepgsql_proc_drop(Oid procOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__DROP, true); ++} ++ ++void ++sepgsql_proc_grant(Oid procOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++} ++ ++Oid ++sepgsql_proc_relabel(Oid procOid, DefElem *newLabel) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ { ++ if (newLabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SELinux is disabled now"))); ++ return InvalidOid; ++ } ++ ++ sid.relid = ProcedureRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); ++ ++ /* db_procedure:{setattr relabelfrom} for older seclabel */ ++ sepgsql_proc_common(procOid, ++ SEPG_DB_PROCEDURE__SETATTR | ++ SEPG_DB_PROCEDURE__RELABELFROM, true); ++ /* db_procedure:{relabelto} for newer seclabel */ ++ sepgsqlClientHasPerms(sid, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_DB_PROCEDURE__RELABELTO, ++ get_func_name(procOid), true); ++ return sid.secid; ++} ++ ++void ++sepgsql_proc_execute(Oid procOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__EXECUTE, true); ++} ++ ++bool ++sepgsql_proc_hint_inlined(HeapTuple protup) ++{ ++ security_context_t newcon; ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return true; ++ ++ if (!sepgsql_proc_common(HeapTupleGetOid(protup), ++ SEPG_DB_PROCEDURE__EXECUTE, false)) ++ return false; ++ /* ++ * If the security context of client is unchange ++ * before or after invocation of the functions, ++ * it is not a trusted procedure, so it can be ++ * inlined due to performance purpose. ++ */ ++ sid = sepgsqlGetTupleSecid(ProcedureRelationId, protup, NULL); ++ ++ newcon = sepgsqlClientCreateLabel(sid, SEPG_CLASS_PROCESS); ++ ++ if (strcmp(sepgsqlGetClientLabel(), newcon) == 0) ++ return true; ++ ++ return false; ++} ++ ++bool ++sepgsql_proc_entrypoint(HeapTuple protup) ++{ ++ security_context_t newcon; ++ sepgsql_sid_t proSid; ++ ++ if (!sepgsqlIsEnabled()) ++ return false; ++ ++ proSid = sepgsqlGetTupleSecid(ProcedureRelationId, ++ protup, NULL); ++ ++ newcon = sepgsqlClientCreateLabel(proSid, SEPG_CLASS_PROCESS); ++ ++ /* Do nothing, if it is not a trusted procedure */ ++ if (strcmp(newcon, sepgsqlGetClientLabel()) == 0) ++ return false; ++ ++ /* check db_procedure:{entrypoint} */ ++ sepgsqlClientHasPerms(proSid, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_DB_PROCEDURE__ENTRYPOINT, ++ NULL, true); ++ ++ /* check process:{transition} */ ++ sepgsqlComputePerms(sepgsqlGetClientLabel(), ++ newcon, ++ SEPG_CLASS_PROCESS, ++ SEPG_PROCESS__TRANSITION, ++ NULL, true); ++ ++ return true; ++} ++ ++char * ++sepgsql_proc_trusted(HeapTuple protup, MemoryContext mcxt) ++{ ++ MemoryContext oldcxt; ++ security_context_t newcon; ++ sepgsql_sid_t proSid; ++ ++ if (!sepgsqlIsEnabled()) ++ return NULL; ++ ++ proSid = sepgsqlGetTupleSecid(ProcedureRelationId, protup, NULL); ++ ++ oldcxt = MemoryContextSwitchTo(mcxt); ++ ++ newcon = sepgsqlClientCreateLabel(proSid, SEPG_CLASS_PROCESS); ++ ++ MemoryContextSwitchTo(oldcxt); ++ ++ return newcon; ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_cast related security hooks ++ * ++ * ------------------------------------------------------------ */ ++Oid ++sepgsql_cast_create(Oid sourceTypOid, Oid targetTypOid, Oid funcOid) ++{ ++ sepgsql_sid_t sid; ++ char audit_buffer[2*NAMEDATALEN+10]; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(CastRelationId); ++ ++ snprintf(audit_buffer, sizeof(audit_buffer), "%s::%s", ++ format_type_be(sourceTypOid), format_type_be(targetTypOid)); ++ ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ audit_buffer, true); ++ ++ if (OidIsValid(funcOid)) ++ sepgsql_proc_common(funcOid, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_cast_drop(Oid castOid) ++{ ++ Form_pg_cast castForm; ++ Relation rel; ++ HeapTuple tuple; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ char audit_buffer[2*NAMEDATALEN+10]; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ rel = heap_open(CastRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey, ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(castOid)); ++ ++ scan = systable_beginscan(rel, CastOidIndexId, true, ++ SnapshotNow, 1, &skey); ++ tuple = systable_getnext(scan); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "could not find tuple for cast: %u", castOid); ++ ++ castForm = (Form_pg_cast) GETSTRUCT(tuple); ++ ++ snprintf(audit_buffer, sizeof(audit_buffer), "%s::%s", ++ format_type_be(castForm->castsource), ++ format_type_be(castForm->casttarget)); ++ ++ sid = sepgsqlGetTupleSecid(CastRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ audit_buffer, true); ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, AccessShareLock); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_conversion related security hooks ++ * ++ * ------------------------------------------------------------ */ ++Oid ++sepgsql_conversion_create(const char *convName, Oid nspOid, Oid procOid) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(ConversionRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ convName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} */ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_conversion_alter(Oid convOid, const char *newName) ++{ ++ Form_pg_conversion convForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(CONVOID, ++ ObjectIdGetDatum(convOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for conversion %u", convOid); ++ convForm = (Form_pg_conversion) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(ConversionRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(convForm->conname), true); ++ if (newName) ++ { ++ Oid nspOid = convForm->connamespace; ++ ++ sepgsql_schema_common(nspOid, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsql_conversion_drop(Oid convOid) ++{ ++ Form_pg_conversion convForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(CONVOID, ++ ObjectIdGetDatum(convOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for conversion %u", convOid); ++ convForm = (Form_pg_conversion) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(ConversionRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(convForm->conname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(convForm->connamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_foreign_data_wrapper related security hooks ++ * ++ * ------------------------------------------------------------ */ ++static bool ++sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort) ++{ ++ Form_pg_foreign_data_wrapper fdwForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ bool rc; ++ ++ tuple = SearchSysCache(FOREIGNDATAWRAPPEROID, ++ ObjectIdGetDatum(fdwOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for FDW: %u", fdwOid); ++ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(ForeignDataWrapperRelationId, tuple, &tclass); ++ rc = sepgsqlClientHasPerms(sid, tclass, required, ++ NameStr(fdwForm->fdwname), abort); ++ ReleaseSysCache(tuple); ++ ++ return rc; ++} ++ ++Oid ++sepgsql_fdw_create(const char *fdwName, Oid fdwValidator) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(ForeignDataWrapperRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ fdwName, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(fdwValidator)) ++ sepgsql_proc_common(fdwValidator, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_fdw_alter(Oid fdwOid, Oid newValidator) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(newValidator)) ++ sepgsql_proc_common(newValidator, SEPG_DB_PROCEDURE__INSTALL, true); ++} ++ ++void ++sepgsql_fdw_drop(Oid fdwOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__DELETE, true); ++} ++ ++void ++sepgsql_fdw_grant(Oid fdwOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_foreign_server related security hooks ++ * ++ * ------------------------------------------------------------ */ ++static bool ++sepgsql_foreign_server_common(Oid fsrvOid, uint32 required, bool abort) ++{ ++ Form_pg_foreign_server fsrvForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ bool rc; ++ ++ tuple = SearchSysCache(FOREIGNSERVEROID, ++ ObjectIdGetDatum(fsrvOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for foreign server %u", fsrvOid); ++ fsrvForm = (Form_pg_foreign_server) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(ForeignServerRelationId, tuple, &tclass); ++ rc = sepgsqlClientHasPerms(sid, tclass, required, ++ NameStr(fsrvForm->srvname), abort); ++ ReleaseSysCache(tuple); ++ ++ return rc; ++} ++ ++Oid ++sepgsql_foreign_server_create(const char *fsrvName) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(ForeignServerRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ fsrvName, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_foreign_server_alter(Oid fsrvOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__UPDATE, true); ++} ++ ++void ++sepgsql_foreign_server_drop(Oid fsrvOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__DELETE, true); ++} ++ ++void ++sepgsql_foreign_server_grant(Oid fsrvOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__UPDATE, true); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_language related security hooks ++ * ++ * ------------------------------------------------------------ */ ++static bool ++sepgsql_language_common(Oid langOid, uint32 required, bool abort) ++{ ++ Form_pg_language langForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ bool rc; ++ ++ tuple = SearchSysCache(LANGOID, ++ ObjectIdGetDatum(langOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for language %u", langOid); ++ langForm = (Form_pg_language) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(LanguageRelationId, tuple, &tclass); ++ rc = sepgsqlClientHasPerms(sid, tclass, required, ++ NameStr(langForm->lanname), abort); ++ ++ ReleaseSysCache(tuple); ++ ++ return rc; ++} ++ ++Oid ++sepgsql_language_create(const char *langName, Oid handlerOid, Oid validatorOid) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(LanguageRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, langName, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(handlerOid)) ++ sepgsql_proc_common(handlerOid, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(validatorOid)) ++ sepgsql_proc_common(validatorOid, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_language_alter(Oid langOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_language_common(langOid, SEPG_DB_TUPLE__UPDATE, true); ++} ++ ++void ++sepgsql_language_drop(Oid langOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_language_common(langOid, SEPG_DB_TUPLE__DELETE, true); ++} ++ ++void ++sepgsql_language_grant(Oid langOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_language_common(langOid, SEPG_DB_TUPLE__UPDATE, true); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_largeobject related security hooks ++ * (need to backport v8.5 feature) ++ * ------------------------------------------------------------ */ ++static bool ++sepgsql_largeobject_common(Oid loid, uint32 required, Snapshot snapshot) ++{ ++ Relation rel; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ char auname[64]; ++ bool rc; ++ ++ rel = heap_open(LargeObjectMetadataRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey, ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(loid)); ++ ++ scan = systable_beginscan(rel, LargeObjectMetadataOidIndexId, ++ true, snapshot, 1, &skey); ++ ++ tuple = systable_getnext(scan); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "largeobject %u lookup failed", loid); ++ ++ snprintf(auname, sizeof(auname), "blob:%u", loid); ++ ++ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass); ++ ++ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, true); ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, AccessShareLock); ++ ++ return rc; ++} ++ ++Oid ++sepgsql_largeobject_create(Oid loid, Value *secLabel) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ if (!secLabel) ++ sid = sepgsqlGetDefaultBlobSecid(MyDatabaseId); ++ else ++ { ++ sid.relid = LargeObjectMetadataRelationId; ++ sid.secid = securityTransSecLabelIn(sid.relid, strVal(secLabel)); ++ } ++ ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__CREATE, ++ NULL, true); ++ return sid.secid; ++} ++ ++void ++sepgsql_largeobject_alter(Oid loid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_largeobject_common(loid, SEPG_DB_BLOB__SETATTR, SnapshotNow); ++} ++ ++void ++sepgsql_largeobject_drop(Oid loid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_largeobject_common(loid, SEPG_DB_BLOB__DROP, SnapshotNow); ++} ++ ++void ++sepgsql_largeobject_read(Oid loid, Snapshot snapshot) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_largeobject_common(loid, SEPG_DB_BLOB__READ, snapshot); ++} ++ ++void ++sepgsql_largeobject_write(Oid loid, Snapshot snapshot) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_largeobject_common(loid, SEPG_DB_BLOB__WRITE, snapshot); ++} ++ ++void ++sepgsql_largeobject_export(Oid loid, const char *filename) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_largeobject_common(loid, ++ SEPG_DB_BLOB__READ | ++ SEPG_DB_BLOB__EXPORT, SnapshotNow); ++ ++ sepgsql_file_write(filename); ++} ++ ++Oid ++sepgsql_largeobject_import(Oid loid, const char *filename) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultBlobSecid(MyDatabaseId); ++ ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__CREATE | ++ SEPG_DB_BLOB__WRITE | ++ SEPG_DB_BLOB__IMPORT, ++ NULL, true); ++ ++ sepgsql_file_read(filename); ++ ++ return sid.secid; ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_opclass related security hooks ++ * ++ * ------------------------------------------------------------ */ ++Oid ++sepgsql_opclass_create(const char *opcName, Oid nspOid) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(OperatorClassRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ opcName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_opclass_alter(Oid opcOid, const char *newName) ++{ ++ Form_pg_opclass opcForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(CLAOID, ++ ObjectIdGetDatum(opcOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for opclass %u", opcOid); ++ opcForm = (Form_pg_opclass) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(OperatorClassRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(opcForm->opcname), true); ++ ++ /* db_schema:{add_name remove_name} */ ++ if (newName) ++ { ++ sepgsql_schema_common(opcForm->opcnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsql_opclass_drop(Oid opcOid) ++{ ++ Form_pg_opclass opcForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(CLAOID, ++ ObjectIdGetDatum(opcOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for opclass %u", opcOid); ++ opcForm = (Form_pg_opclass) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(OperatorClassRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(opcForm->opcname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(opcForm->opcnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_opfamily related security hooks ++ * ++ * ------------------------------------------------------------ */ ++Oid ++sepgsql_opfamily_create(const char *opfName, Oid nspOid) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(OperatorFamilyRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ opfName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_opfamily_alter(Oid opfOid, const char *newName) ++{ ++ Form_pg_opfamily opfForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(OPFAMILYOID, ++ ObjectIdGetDatum(opfOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for operator family: %u", opfOid); ++ opfForm = (Form_pg_opfamily) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(opfForm->opfname), true); ++ if (newName) ++ { ++ sepgsql_schema_common(opfForm->opfnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsql_opfamily_drop(Oid opfOid) ++{ ++ Form_pg_opfamily opfForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(OPFAMILYOID, ++ ObjectIdGetDatum(opfOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for operator family: %u", opfOid); ++ opfForm = (Form_pg_opfamily) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(opfForm->opfname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(opfForm->opfnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsql_opfamily_add_operator(Oid opfOid, Oid operOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* currently, do nothing here */ ++} ++ ++void ++sepgsql_opfamily_add_procedure(Oid opfOid, Oid procOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* ++ * Note that db_tuple:{setattr} is already checked at the ++ * earlier phase, so db_procedure:{install} is only needed. ++ */ ++ if (OidIsValid(procOid)) ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_operator related security hooks ++ * ++ * ------------------------------------------------------------ */ ++static bool ++sepgsql_operator_common(Oid oprOid, uint32 required, bool abort) ++{ ++ Form_pg_operator oprForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ bool rc; ++ ++ tuple = SearchSysCache(OPEROID, ++ ObjectIdGetDatum(oprOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for operator: %u", oprOid); ++ oprForm = (Form_pg_operator) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(OperatorRelationId, tuple, &tclass); ++ rc = sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(oprForm->oprname), abort); ++ ++ ReleaseSysCache(tuple); ++ ++ return rc; ++} ++ ++Oid ++sepgsql_operator_create(const char *oprName, Oid oprOid, Oid nspOid, ++ Oid codeFn, Oid restFn, Oid joinFn) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint32 required; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ if (!OidIsValid(oprOid)) ++ { ++ sid = sepgsqlGetDefaultTupleSecid(OperatorRelationId); ++ required = SEPG_DB_TUPLE__INSERT; ++ } ++ else ++ { ++ tuple = SearchSysCache(OPEROID, ++ ObjectIdGetDatum(oprOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for operator %u", oprOid); ++ ++ sid = sepgsqlGetTupleSecid(OperatorRelationId, tuple, NULL); ++ ++ ReleaseSysCache(tuple); ++ ++ required = SEPG_DB_TUPLE__UPDATE; ++ } ++ ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ required, oprName, true); ++ ++ /* db_schema:{add_name} checks */ ++ if (!OidIsValid(oprOid)) ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} checks */ ++ if (OidIsValid(codeFn)) ++ sepgsql_proc_common(codeFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(restFn)) ++ sepgsql_proc_common(restFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(joinFn)) ++ sepgsql_proc_common(joinFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_operator_alter(Oid oprOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_operator_common(oprOid, SEPG_DB_TUPLE__UPDATE, true); ++} ++ ++void ++sepgsql_operator_drop(Oid oprOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_operator_common(oprOid, SEPG_DB_TUPLE__DELETE, true); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_rewrite related security hooks ++ * ++ * ------------------------------------------------------------ */ ++void ++sepgsql_rule_create(Oid relOid, const char *ruleName) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++} ++ ++void ++sepgsql_rule_drop(Oid relOid, const char *ruleName) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_trigger related security hooks ++ * ++ * ------------------------------------------------------------ */ ++void ++sepgsql_trigger_create(Oid relOid, const char *trigName, Oid procOid) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* db_table:{setattr} */ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++ ++ /* db_procedure:{install} */ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); ++} ++ ++void ++sepgsql_trigger_alter(Oid relOid, const char *trigName) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* db_table:{setattr} */ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++} ++ ++void ++sepgsql_trigger_drop(Oid relOid, const char *trigName) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* db_table:{setattr} */ ++ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_type related security hooks ++ * ++ * ------------------------------------------------------------ */ ++Oid ++sepgsql_ts_config_create(const char *cfgName, Oid nspOid) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(TSConfigRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ cfgName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_ts_config_alter(Oid cfgOid, const char *newName) ++{ ++ Form_pg_ts_config cfgForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSCONFIGOID, ++ ObjectIdGetDatum(cfgOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search dictionary %u", cfgOid); ++ cfgForm = (Form_pg_ts_config) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSConfigRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(cfgForm->cfgname), true); ++ if (newName) ++ { ++ sepgsql_schema_common(cfgForm->cfgnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsql_ts_config_drop(Oid cfgOid) ++{ ++ Form_pg_ts_config cfgForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSCONFIGOID, ++ ObjectIdGetDatum(cfgOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search dictionary %u", cfgOid); ++ cfgForm = (Form_pg_ts_config) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSConfigRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(cfgForm->cfgname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(cfgForm->cfgnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_type related security hooks ++ * ++ * ------------------------------------------------------------ */ ++Oid ++sepgsql_ts_dict_create(const char *dictName, Oid nspOid) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(TSDictionaryRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ dictName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_ts_dict_alter(Oid dictOid, const char *newName) ++{ ++ Form_pg_ts_dict dictForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSDICTOID, ++ ObjectIdGetDatum(dictOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); ++ dictForm = (Form_pg_ts_dict) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSDictionaryRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(dictForm->dictname), true); ++ ++ /* db_schema:{add_name remove_name} */ ++ if (newName) ++ { ++ sepgsql_schema_common(dictForm->dictnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsql_ts_dict_drop(Oid dictOid) ++{ ++ Form_pg_ts_dict dictForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSDICTOID, ++ ObjectIdGetDatum(dictOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); ++ dictForm = (Form_pg_ts_dict) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSDictionaryRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(dictForm->dictname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(dictForm->dictnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_type related security hooks ++ * ++ * ------------------------------------------------------------ */ ++Oid ++sepgsql_ts_parser_create(const char *prsName, Oid nspOid, ++ Oid startFn, Oid tokenFn, Oid sendFn, ++ Oid headlineFn, Oid lextypeFn) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(TSParserRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ prsName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(startFn)) ++ sepgsql_proc_common(startFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(tokenFn)) ++ sepgsql_proc_common(tokenFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(sendFn)) ++ sepgsql_proc_common(sendFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(headlineFn)) ++ sepgsql_proc_common(headlineFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(lextypeFn)) ++ sepgsql_proc_common(lextypeFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_ts_parser_alter(Oid prsOid, const char *newName) ++{ ++ Form_pg_ts_parser prsForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSPARSEROID, ++ ObjectIdGetDatum(prsOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search parser %u", prsOid); ++ ++ prsForm = (Form_pg_ts_parser) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSParserRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(prsForm->prsname), true); ++ if (newName) ++ { ++ sepgsql_schema_common(prsForm->prsnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsql_ts_parser_drop(Oid prsOid) ++{ ++ Form_pg_ts_parser prsForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSPARSEROID, ++ ObjectIdGetDatum(prsOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search parser %u", prsOid); ++ ++ prsForm = (Form_pg_ts_parser) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSParserRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(prsForm->prsname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(prsForm->prsnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_type related security hooks ++ * ++ * ------------------------------------------------------------ */ ++Oid ++sepgsql_ts_template_create(const char *tmplName, Oid nspOid, ++ Oid initFn, Oid lexizeFn) ++{ ++ sepgsql_sid_t sid; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ sid = sepgsqlGetDefaultTupleSecid(TSTemplateRelationId); ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ tmplName, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(initFn)) ++ sepgsql_proc_common(initFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(lexizeFn)) ++ sepgsql_proc_common(lexizeFn, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_ts_template_alter(Oid tmplOid, const char *newName) ++{ ++ Form_pg_ts_template tmplForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSTEMPLATEOID, ++ ObjectIdGetDatum(tmplOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search template %u", tmplOid); ++ tmplForm = (Form_pg_ts_template) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSTemplateRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(tmplForm->tmplname), true); ++ if (newName) ++ { ++ sepgsql_schema_common(tmplForm->tmplnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsql_ts_template_drop(Oid tmplOid) ++{ ++ Form_pg_ts_template tmplForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TSTEMPLATEOID, ++ ObjectIdGetDatum(tmplOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search template %u", tmplOid); ++ tmplForm = (Form_pg_ts_template) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TSTemplateRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(tmplForm->tmplname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(tmplForm->tmplnamespace, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Pg_type related security hooks ++ * ++ * ------------------------------------------------------------ */ ++Oid ++sepgsql_type_create(const char *typName, HeapTuple oldTup, Oid nspOid, ++ Oid inputProc, Oid outputProc, Oid recvProc, Oid sendProc, ++ Oid modinProc, Oid modoutProc, Oid analyzeProc) ++{ ++ sepgsql_sid_t sid; ++ uint32 required; ++ ++ if (!sepgsqlIsEnabled()) ++ return InvalidOid; ++ ++ if (!HeapTupleIsValid(oldTup)) ++ { ++ sid = sepgsqlGetDefaultTupleSecid(TypeRelationId); ++ required = SEPG_DB_TUPLE__INSERT; ++ } ++ else ++ { ++ sid = sepgsqlGetTupleSecid(TypeRelationId, oldTup, NULL); ++ required = SEPG_DB_TUPLE__UPDATE; ++ } ++ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, ++ required, typName, true); ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(inputProc)) ++ sepgsql_proc_common(inputProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(outputProc)) ++ sepgsql_proc_common(outputProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(recvProc)) ++ sepgsql_proc_common(recvProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(sendProc)) ++ sepgsql_proc_common(sendProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(modinProc)) ++ sepgsql_proc_common(modinProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(modoutProc)) ++ sepgsql_proc_common(modoutProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(analyzeProc)) ++ sepgsql_proc_common(analyzeProc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return sid.secid; ++} ++ ++void ++sepgsql_type_alter(Oid typOid, const char *newName, Oid newNsp) ++{ ++ Form_pg_type typForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TYPEOID, ++ ObjectIdGetDatum(typOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for type: %u", typOid); ++ typForm = (Form_pg_type) GETSTRUCT(tuple); ++ ++ sid = sepgsqlGetTupleSecid(TypeRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__UPDATE, ++ NameStr(typForm->typname), true); ++ ++ if (newName || OidIsValid(newNsp)) ++ { ++ Oid oldNsp = typForm->typnamespace; ++ ++ if (!OidIsValid(newNsp)) ++ { ++ sepgsql_schema_common(oldNsp, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ } ++ else ++ { ++ sepgsql_schema_common(oldNsp, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); ++ } ++ } ++ ReleaseSysCache(tuple); ++} ++ ++void ++sepgsql_type_drop(Oid typOid) ++{ ++ Form_pg_type typForm; ++ HeapTuple tuple; ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ tuple = SearchSysCache(TYPEOID, ++ ObjectIdGetDatum(typOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for type: %u", typOid); ++ typForm = (Form_pg_type) GETSTRUCT(tuple); ++ ++ if (typForm->typtype == TYPTYPE_COMPOSITE || ++ (typForm->typtype == TYPTYPE_BASE && OidIsValid(typForm->typarray))) ++ { ++ /* ++ * No need to check for composite type and implicitly ++ * declared array type here. ++ */ ++ ReleaseSysCache(tuple); ++ return; ++ } ++ ++ sid = sepgsqlGetTupleSecid(TypeRelationId, tuple, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__DELETE, ++ NameStr(typForm->typname), true); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(typForm->typnamespace, ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ ReleaseSysCache(tuple); ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Misc system object related security hooks ++ * ++ * ------------------------------------------------------------ */ ++ ++void ++sepgsql_sysobj_drop(const ObjectAddress *object) ++{ ++ switch (object->classId) ++ { ++ case RelationRelationId: ++ if (object->objectSubId == 0) ++ sepgsql_relation_drop(object->objectId); ++ else ++ sepgsql_attribute_drop(object->objectId, ++ object->objectSubId); ++ break; ++ ++ case ProcedureRelationId: ++ sepgsql_proc_drop(object->objectId); ++ break; ++ ++ case TypeRelationId: ++ sepgsql_type_drop(object->objectId); ++ break; ++ ++ case CastRelationId: ++ sepgsql_cast_drop(object->objectId); ++ break; ++ ++ case ConversionRelationId: ++ sepgsql_conversion_drop(object->objectId); ++ break; ++ ++ case LanguageRelationId: ++ sepgsql_language_drop(object->objectId); ++ break; ++ ++ case OperatorRelationId: ++ sepgsql_operator_drop(object->objectId); ++ break; ++ ++ case OperatorClassRelationId: ++ sepgsql_opclass_drop(object->objectId); ++ break; ++ ++ case OperatorFamilyRelationId: ++ sepgsql_opfamily_drop(object->objectId); ++ break; ++ ++ case NamespaceRelationId: ++ sepgsql_schema_drop(object->objectId); ++ break; ++ ++ case TSParserRelationId: ++ sepgsql_ts_parser_drop(object->objectId); ++ break; ++ ++ case TSDictionaryRelationId: ++ sepgsql_ts_dict_drop(object->objectId); ++ break; ++ ++ case TSTemplateRelationId: ++ sepgsql_ts_template_drop(object->objectId); ++ break; ++ ++ case TSConfigRelationId: ++ sepgsql_ts_config_drop(object->objectId); ++ break; ++ ++ case AuthIdRelationId: ++ break; ++ ++ case DatabaseRelationId: ++ sepgsql_database_drop(object->objectId); ++ break; ++ ++ case TableSpaceRelationId: ++ break; ++ ++ case ForeignDataWrapperRelationId: ++ sepgsql_fdw_drop(object->objectId); ++ break; ++ ++ case ForeignServerRelationId: ++ sepgsql_foreign_server_drop(object->objectId); ++ break; ++ ++ case UserMappingRelationId: ++ break; ++ ++ default: ++ /* do nothing */ ++ break; ++ } ++} ++ ++/* ------------------------------------------------------------ * ++ * ++ * Filesystem object related security hooks ++ * ++ * ------------------------------------------------------------ */ ++static char * ++sepgsql_getfilecon(const char *path) ++{ ++ security_context_t context; ++ char *result; ++ ++ if (getfilecon_raw(path, &context) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not get context of \"%s\": %m", path))); ++ ++ PG_TRY(); ++ { ++ result = pstrdup(context); ++ } ++ PG_CATCH(); ++ { ++ freecon(context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(context); ++ ++ return result; ++} ++ ++static void ++sepgsql_file_common(const char *filename, uint32 required, bool may_create) ++{ ++ struct stat stbuf; ++ ++ if (stat(filename, &stbuf) == 0) ++ { ++ uint16 tclass; ++ ++ /* ++ * Get file object class ++ */ ++ if (S_ISDIR(stbuf.st_mode)) ++ tclass = SEPG_CLASS_DIR; ++ else if (S_ISCHR(stbuf.st_mode)) ++ tclass = SEPG_CLASS_CHR_FILE; ++ else if (S_ISBLK(stbuf.st_mode)) ++ tclass = SEPG_CLASS_BLK_FILE; ++ else if (S_ISFIFO(stbuf.st_mode)) ++ tclass = SEPG_CLASS_FIFO_FILE; ++ else if (S_ISLNK(stbuf.st_mode)) ++ tclass = SEPG_CLASS_LNK_FILE; ++ else if (S_ISSOCK(stbuf.st_mode)) ++ tclass = SEPG_CLASS_SOCK_FILE; ++ else ++ tclass = SEPG_CLASS_FILE; ++ ++ /* ++ * Check permission (no cached operation) ++ */ ++ sepgsqlComputePerms(sepgsqlGetClientLabel(), ++ sepgsql_getfilecon(filename), ++ tclass, required, ++ filename, true); ++ } ++ else if (may_create) ++ { ++ /* ++ * If the required file is not found, we check permission to ++ * create a new file and required permission on the new file. ++ */ ++ security_context_t dcontext; ++ security_context_t ncontext; ++ char *copy = pstrdup(filename); ++ ++ /* ++ * Compute a security context for the new file ++ */ ++ dcontext = sepgsql_getfilecon(dirname(copy)); ++ ++ ncontext = sepgsqlComputeCreate(sepgsqlGetServerLabel(), ++ dcontext, ++ SEPG_CLASS_FILE); ++ /* ++ * Check permission (no cached operation) ++ */ ++ required |= SEPG_FILE__CREATE; ++ ++ sepgsqlComputePerms(sepgsqlGetClientLabel(), ++ sepgsql_getfilecon(filename), ++ SEPG_CLASS_FILE, ++ required, filename, true); ++ } ++ else ++ { ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not stat file \"%s\": %m", filename))); ++ } ++} ++ ++void ++sepgsql_file_stat(const char *filename) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_file_common(filename, SEPG_FILE__GETATTR, false); ++} ++ ++void ++sepgsql_file_read(const char *filename) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_file_common(filename, SEPG_FILE__READ, false); ++} ++ ++void ++sepgsql_file_write(const char *filename) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ sepgsql_file_common(filename, SEPG_FILE__WRITE, true); ++} ++ ++/* ++ * TODO: add check for pg_ls_dir() ++ */ +diff --git a/src/backend/security/sepgsql/checker.c b/src/backend/security/sepgsql/checker.c +new file mode 100644 +index 0000000..9e573c3 +--- /dev/null ++++ b/src/backend/security/sepgsql/checker.c +@@ -0,0 +1,432 @@ ++/* ++ * src/backend/security/sepgsql/checker.c ++ * walks on given Query tree and applies checks ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++ ++#include "access/sysattr.h" ++#include "catalog/catalog.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_security.h" ++#include "miscadmin.h" ++#include "security/sepgsql.h" ++#include "storage/bufmgr.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++ ++/* ++ * fixupWholeRowReference ++ */ ++static Bitmapset * ++fixupWholeRowReference(Oid relid, int nattrs, Bitmapset *columns) ++{ ++ Bitmapset *result; ++ AttrNumber attno; ++ ++ attno = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber; ++ ++ if (!bms_is_member(attno, columns)) ++ return columns; /* no need to fixup */ ++ ++ result = bms_copy(columns); ++ result = bms_del_member(result, attno); ++ ++ for (attno=1; attno <= nattrs; attno++) ++ { ++ Form_pg_attribute attform; ++ HeapTuple atttup; ++ ++ atttup = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(relid), ++ Int16GetDatum(attno), ++ 0, 0); ++ if (!HeapTupleIsValid(atttup)) ++ continue; ++ ++ attform = (Form_pg_attribute) GETSTRUCT(atttup); ++ if (!attform->attisdropped) ++ { ++ int cindex = attno - FirstLowInvalidHeapAttributeNumber; ++ result = bms_add_member(result, cindex); ++ } ++ ReleaseSysCache(atttup); ++ } ++ ++ return result; ++} ++ ++/* ++ * checkTabelColumnPerms ++ * This functions applies table/column level permissions for ++ * all the appeared ones in user's query, and raises an error ++ * if violated. ++ * It also applies a few hardwired policy which prevent to ++ * modified some of system catalogs. ++ */ ++static void ++checkTabelColumnPerms(Oid relid, Bitmapset *selected, Bitmapset *modified, ++ access_vector_t required) ++{ ++ Bitmapset *columns; ++ Bitmapset *selected_ex; ++ Bitmapset *modified_ex; ++ Form_pg_class relForm; ++ HeapTuple reltup; ++ sepgsql_sid_t relsid; ++ sepgsql_sid_t attsid; ++ AttrNumber attno; ++ uint16 tclass; ++ ++ /* ++ * Hardwired Policy: ++ * SE-PostgreSQL enforces that clients cannot modify system ++ * catalogs and access toast values using DML statements, ++ * except initial setting up phase. ++ */ ++ if (sepgsqlGetEnforce()) ++ { ++ if (IsSystemNamespace(get_rel_namespace(relid)) && ++ (required & (SEPG_DB_TABLE__UPDATE | ++ SEPG_DB_TABLE__INSERT | ++ SEPG_DB_TABLE__DELETE)) != 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL prevents to modidy \"%s\"", ++ get_rel_name(relid)))); ++ if (get_rel_relkind(relid) == RELKIND_TOASTVALUE) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL prevents to access \"%s\"", ++ get_rel_name(relid)))); ++ } ++ ++ /* ++ * Check db_table:{...} or db_sequence permissions ++ */ ++ reltup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(reltup)) ++ elog(ERROR, "SELinux: cache lookup failed for relation %u", relid); ++ ++ relForm = (Form_pg_class) GETSTRUCT(reltup); ++ ++ relsid = sepgsqlGetTupleSecid(RelationRelationId, reltup, &tclass); ++ ++ if (tclass != SEPG_CLASS_DB_TABLE) ++ { ++ /* check db_sequence:{xxx} permission */ ++ if (tclass == SEPG_CLASS_DB_SEQUENCE) ++ { ++ if (required & SEPG_DB_TABLE__SELECT) ++ { ++ sepgsqlClientHasPerms(relsid, tclass, ++ SEPG_DB_SEQUENCE__GET_VALUE, ++ NameStr(relForm->relname), true); ++ } ++ } ++ ReleaseSysCache(reltup); ++ return; ++ } ++ sepgsqlClientHasPerms(relsid, tclass, required, ++ NameStr(relForm->relname), true); ++ ++ /* ++ * Check db_column:{...} permissions ++ */ ++ selected_ex = fixupWholeRowReference(relid, relForm->relnatts, selected); ++ modified_ex = fixupWholeRowReference(relid, relForm->relnatts, modified); ++ columns = bms_union(selected_ex, modified_ex); ++ ++ while ((attno = bms_first_member(columns)) >= 0) ++ { ++ Form_pg_attribute attForm; ++ HeapTuple atttup; ++ uint32 attperms = 0; ++ char auname[2 * NAMEDATALEN + 3]; ++ ++ if (bms_is_member(attno, selected_ex)) ++ attperms |= SEPG_DB_COLUMN__SELECT; ++ if (bms_is_member(attno, modified_ex)) ++ { ++ if (required & SEPG_DB_TABLE__UPDATE) ++ attperms |= SEPG_DB_COLUMN__UPDATE; ++ if (required & SEPG_DB_TABLE__INSERT) ++ attperms |= SEPG_DB_COLUMN__INSERT; ++ } ++ if (attperms == 0) ++ continue; ++ ++ /* remove the attribute number offset */ ++ attno += FirstLowInvalidHeapAttributeNumber; ++ atttup = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(relid), ++ Int16GetDatum(attno), ++ 0, 0); ++ if (!HeapTupleIsValid(atttup)) ++ elog(ERROR, "cache lookup failed for attribute %d of relation %u", ++ attno, relid); ++ ++ attForm = (Form_pg_attribute) GETSTRUCT(atttup); ++ if (attForm->attisdropped) ++ elog(ERROR, "attribute %d of relation %u does not exist", ++ attno, relid); ++ ++ snprintf(auname, sizeof(auname), "%s.%s", ++ NameStr(relForm->relname), ++ NameStr(attForm->attname)); ++ attsid = sepgsqlGetTupleSecid(AttributeRelationId, ++ atttup, &tclass); ++ sepgsqlClientHasPerms(attsid, tclass, attperms, auname, true); ++ ++ ReleaseSysCache(atttup); ++ } ++ ++ ReleaseSysCache(reltup); ++ ++ if (selected_ex != selected) ++ bms_free(selected_ex); ++ ++ if (modified_ex != modified) ++ bms_free(modified_ex); ++ ++ bms_free(columns); ++} ++ ++/* ++ * sepgsqlCheckQueryPerms ++ * It checks permission for all the required tables/columns on ++ * generic user queries. ++ */ ++void ++sepgsqlCheckRTEPerms(RangeTblEntry *rte) ++{ ++ access_vector_t required = 0; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ if (rte->rtekind != RTE_RELATION) ++ return; ++ ++ if (rte->requiredPerms & ACL_SELECT) ++ required |= SEPG_DB_TABLE__SELECT; ++ if (rte->requiredPerms & ACL_INSERT) ++ required |= SEPG_DB_TABLE__INSERT; ++ if (rte->requiredPerms & ACL_UPDATE) ++ { ++ /* ++ * ACL_SELECT_FOR_UPDATE is defined as an aliase of ACL_UPDATE, ++ * so we cannot determine whether the given relation is accessed ++ * with UPDATE statement or SELECT FOR SHARE/UPDATE immediately. ++ * UPDATE statements set a bit on rte->modifiedCols at least, ++ * so we use it as a watermark. ++ */ ++ if (!bms_is_empty(rte->modifiedCols)) ++ required |= SEPG_DB_TABLE__UPDATE; ++ else ++ required |= SEPG_DB_TABLE__LOCK; ++ } ++ if (rte->requiredPerms & ACL_DELETE) ++ required |= SEPG_DB_TABLE__DELETE; ++ ++ if (required == 0) ++ return; ++ ++ checkTabelColumnPerms(rte->relid, ++ rte->selectedCols, ++ rte->modifiedCols, ++ required); ++} ++ ++/* ++ * sepgsqlCheckCopyTable ++ * It checks permissions on COPY TO/FROM. ++ */ ++void ++sepgsqlCheckCopyTable(Relation rel, List *attnumlist, bool is_from) ++{ ++ Bitmapset *selected = NULL; ++ Bitmapset *modified = NULL; ++ ListCell *l; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* all checkes are done in sepgsqlCheckRTEPerms */ ++ if (!rel) ++ return; ++ ++ foreach (l, attnumlist) ++ { ++ AttrNumber attno = lfirst_int(l); ++ ++ attno -= FirstLowInvalidHeapAttributeNumber; ++ if (is_from) ++ modified = bms_add_member(modified, attno); ++ else ++ selected = bms_add_member(selected, attno); ++ } ++ ++ checkTabelColumnPerms(RelationGetRelid(rel), ++ selected, modified, ++ is_from ? SEPG_DB_TABLE__INSERT ++ : SEPG_DB_TABLE__SELECT); ++} ++ ++/* ++ * sepgsqlExecScan ++ * makes a decision on the given tuple. ++ */ ++bool ++sepgsqlExecScan(Relation rel, HeapTuple tuple, uint32 required, bool abort) ++{ ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled() || ++ !required || ++ RelationGetForm(rel)->relkind != RELKIND_RELATION || ++ RelationGetRelid(rel) == SecurityRelationId) ++ return true; ++ ++ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass); ++ /* ++ * Insert/Delete to an external attribute is equivalent to ++ * the set-attribute on the master ++ */ ++ if (sid.relid != RelationGetRelid(rel) && ++ (required & (SEPG_DB_TUPLE__INSERT | SEPG_DB_TUPLE__DELETE))) ++ { ++ required &= ~(SEPG_DB_TUPLE__INSERT | SEPG_DB_TUPLE__DELETE); ++ required |= SEPG_DB_TUPLE__UPDATE; ++ } ++ ++ return sepgsqlClientHasPerms(sid, tclass, required, NULL, abort); ++} ++ ++uint32 ++sepgsqlSetupTuplePerms(RangeTblEntry *rte) ++{ ++ AclMode perms = 0; ++ ++ if (!sepgsqlIsEnabled()) ++ return 0; ++ ++ if (rte->rtekind != RTE_RELATION) ++ return 0; ++ ++ if (rte->requiredPerms & ACL_SELECT) ++ perms |= SEPG_DB_TUPLE__SELECT; ++ if (rte->requiredPerms & ACL_UPDATE && !bms_is_empty(rte->modifiedCols)) ++ perms |= SEPG_DB_TUPLE__UPDATE; ++ if (rte->requiredPerms & ACL_DELETE) ++ perms |= SEPG_DB_TUPLE__DELETE; ++ ++ /* ++ * Special case in pg_largeobject ++ */ ++ if (rte->relid == LargeObjectRelationId && ++ bms_is_member(Anum_pg_largeobject_data ++ - FirstLowInvalidHeapAttributeNumber, ++ rte->selectedCols)) ++ perms |= SEPG_DB_BLOB__READ; ++ ++ return perms; ++} ++ ++/* ++ * sepgsqlHeapTupleInsert ++ * It assigns a default security label, if no explicit security labels ++ * were given. In addition, it also checks db_tuple:{insert} for the ++ * tuple newly inserted, when it invoked from user's query. ++ */ ++void ++sepgsqlHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal) ++{ ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* ++ * Assign a default security label, if necessary ++ */ ++ if (HeapTupleHasSecid(newtup) && ++ !OidIsValid(HeapTupleGetSecid(newtup))) ++ sepgsqlSetDefaultSecid(rel, newtup); ++ ++ /* ++ * It does not check permission for the new tuples ++ * inserted by system internal stuff using ++ * simple_heap_insert(); ++ */ ++ if (internal) ++ return; ++ ++ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), ++ newtup, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, SEPG_DB_TUPLE__INSERT, NULL, true); ++} ++ ++/* ++ * sepgsqlHeapTupleUpdate ++ * It checks db_tuple:{relabelfrom relabelto} permission on ++ * the user queries. (Please note that it does not check ++ * system internal stuff via simple_heap_update) ++ */ ++void ++sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup) ++{ ++ Oid secid; ++ HeapTupleData oldtup; ++ Buffer oldbuf; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* ++ * heap_update() preserves the original security label ++ * of the given tuple, if no explicit security label ++ * is assigned on the newer version. ++ * In this case, db_tuple:{update} is already checked ++ * at the sepgsqlExecScan() hook, so we don't need to ++ * check anything more. ++ */ ++ secid = HeapTupleGetSecid(newtup); ++ if (!OidIsValid(secid)) ++ return; ++ ++ /* ++ * User gave an explicit security label ++ */ ++ ItemPointerCopy(otid, &oldtup.t_self); ++ if (!heap_fetch(rel, SnapshotAny, &oldtup, &oldbuf, false, NULL)) ++ elog(ERROR, "failed to fetch old version of the tuple"); ++ ++ if (secid != HeapTupleGetSecid(&oldtup)) ++ { ++ sepgsql_sid_t sid; ++ uint16 tclass; ++ ++ /* db_tuple:{relabelfrom} for older security context */ ++ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), ++ &oldtup, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__RELABELFROM, ++ NULL, true); ++ ++ /* db_tuple:{relabelto} for newer security label */ ++ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), ++ newtup, &tclass); ++ sepgsqlClientHasPerms(sid, tclass, ++ SEPG_DB_TUPLE__RELABELTO, ++ NULL, true); ++ } ++ ReleaseBuffer(oldbuf); ++} +diff --git a/src/backend/security/sepgsql/dummy.c b/src/backend/security/sepgsql/dummy.c +new file mode 100644 +index 0000000..6df24d3 +--- /dev/null ++++ b/src/backend/security/sepgsql/dummy.c +@@ -0,0 +1,79 @@ ++/* ++ * src/backend/utils/sepgsql/dummy.c ++ * A set of stubs when SE-PostgreSQL is not activated ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++ ++#include "security/sepgsql.h" ++ ++static Datum ++unavailable_function(const char *fn_name) ++{ ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("function \"%s\" is not available", fn_name))); ++ PG_RETURN_VOID(); ++} ++ ++Datum ++sepgsql_getcon(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__); ++} ++ ++Datum ++sepgsql_server_getcon(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__); ++} ++ ++Datum ++sepgsql_get_user(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__); ++} ++ ++Datum ++sepgsql_get_role(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__); ++} ++ ++Datum ++sepgsql_get_type(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__); ++} ++ ++Datum ++sepgsql_get_range(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__); ++} ++ ++Datum ++sepgsql_set_user(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__); ++} ++ ++Datum ++sepgsql_set_role(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__); ++} ++ ++Datum ++sepgsql_set_type(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__); ++} ++ ++Datum ++sepgsql_set_range(PG_FUNCTION_ARGS) ++{ ++ return unavailable_function(__FUNCTION__); ++} +diff --git a/src/backend/security/sepgsql/label.c b/src/backend/security/sepgsql/label.c +new file mode 100644 +index 0000000..e91f8c9 +--- /dev/null ++++ b/src/backend/security/sepgsql/label.c +@@ -0,0 +1,1213 @@ ++/* ++ * src/backend/security/sepgsql/label.c ++ * SE-PostgreSQL security label management ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++ ++#include "access/sysattr.h" ++#include "access/xact.h" ++#include "catalog/catalog.h" ++#include "catalog/pg_constraint.h" ++#include "catalog/heap.h" ++#include "catalog/indexing.h" ++#include "catalog/namespace.h" ++#include "catalog/pg_aggregate.h" ++#include "catalog/pg_amop.h" ++#include "catalog/pg_amproc.h" ++#include "catalog/pg_attrdef.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_auth_members.h" ++#include "catalog/pg_authid.h" ++#include "catalog/pg_cast.h" ++#include "catalog/pg_class.h" ++#include "catalog/pg_conversion.h" ++#include "catalog/pg_database.h" ++#include "catalog/pg_description.h" ++#include "catalog/pg_enum.h" ++#include "catalog/pg_foreign_data_wrapper.h" ++#include "catalog/pg_foreign_server.h" ++#include "catalog/pg_inherits.h" ++#include "catalog/pg_language.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_largeobject_metadata.h" ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_opclass.h" ++#include "catalog/pg_operator.h" ++#include "catalog/pg_opfamily.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_rewrite.h" ++#include "catalog/pg_security.h" ++#include "catalog/pg_shdescription.h" ++#include "catalog/pg_statistic.h" ++#include "catalog/pg_tablespace.h" ++#include "catalog/pg_trigger.h" ++#include "catalog/pg_ts_config.h" ++#include "catalog/pg_ts_config_map.h" ++#include "catalog/pg_ts_dict.h" ++#include "catalog/pg_ts_parser.h" ++#include "catalog/pg_ts_template.h" ++#include "catalog/pg_type.h" ++#include "catalog/pg_user_mapping.h" ++#include "miscadmin.h" ++#include "nodes/makefuncs.h" ++#include "security/sepgsql.h" ++#include "storage/fd.h" ++#include "utils/fmgroids.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++ ++/* GUC: to turn on/off row level controls in SE-PostgreSQL */ ++bool sepostgresql_row_level; ++ ++/* GUC parameter to turn on/off mcstrans */ ++bool sepostgresql_mcstrans; ++ ++/* ++ * sepgsqlTupleDescHasSecid ++ * ++ * returns a hint whether we should allocate a field to store ++ * security label on the given relation, or not. ++ */ ++bool ++sepgsqlTupleDescHasSecid(Oid relid, char relkind) ++{ ++ /* ++ * sepgsqlIsEnabled() is not available because it always returns ++ * false in bootstraping mode ++ */ ++ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED || ++ is_selinux_enabled() < 1) ++ return false; ++ ++ if (!OidIsValid(relid)) ++ return sepostgresql_row_level; /* Target of SELECT INTO */ ++ ++ /* These system catalogs always have its secid */ ++ if (relid == DatabaseRelationId || ++ relid == NamespaceRelationId || ++ relid == RelationRelationId || ++ relid == AttributeRelationId || ++ relid == ProcedureRelationId) ++ return true; ++ ++ /* These system catalogs are an external attributes */ ++ if (relid == AggregateRelationId || ++ relid == AccessMethodOperatorRelationId || ++ relid == AccessMethodProcedureRelationId || ++ relid == AttrDefaultRelationId || ++ relid == AuthMemRelationId || ++ relid == ConstraintRelationId || ++ relid == DescriptionRelationId || ++ relid == EnumRelationId || ++ relid == IndexRelationId || ++ relid == InheritsRelationId || ++ relid == LargeObjectRelationId || ++ relid == RewriteRelationId || ++ relid == SecurityRelationId || ++ relid == SharedDescriptionRelationId || ++ relid == StatisticRelationId || ++ relid == TriggerRelationId) ++ return false; ++ ++ return sepostgresql_row_level; ++} ++ ++/* ++ * defaultSecidWithXXXX ++ */ ++static sepgsql_sid_t ++defaultSecidWithDatabase(Oid relOid, Oid datOid, uint16 tclass) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t datSid; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(datOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for database: %u", datOid); ++ ++ datSid.relid = DatabaseRelationId; ++ datSid.secid = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ ++ return sepgsqlClientCreateSecid(datSid, tclass, relOid); ++} ++ ++static sepgsql_sid_t ++defaultSecidWithSchema(Oid relOid, Oid nspOid, uint16 tclass) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t nspSid; ++ ++ tuple = SearchSysCache(NAMESPACEOID, ++ ObjectIdGetDatum(nspOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for schema: %u", nspOid); ++ ++ nspSid.relid = NamespaceRelationId; ++ nspSid.secid = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ ++ return sepgsqlClientCreateSecid(nspSid, tclass, relOid); ++} ++ ++static sepgsql_sid_t ++defaultSecidWithTable(Oid relOid, Oid tblOid, uint16 tclass) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tblSid; ++ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(tblOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation: %u", tblOid); ++ ++ tblSid.relid = RelationRelationId; ++ tblSid.secid = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ ++ return sepgsqlClientCreateSecid(tblSid, tclass, relOid); ++} ++ ++/* ++ * sepgsqlGetDefaultDatabaseSecid ++ * It returns the default security label of a database object. ++ */ ++sepgsql_sid_t ++sepgsqlGetDefaultDatabaseSecid(Oid source_database_oid) ++{ ++ return defaultSecidWithDatabase(DatabaseRelationId, ++ source_database_oid, ++ SEPG_CLASS_DB_DATABASE); ++} ++ ++sepgsql_sid_t ++sepgsqlGetDefaultSchemaSecid(Oid database_oid) ++{ ++ return defaultSecidWithDatabase(NamespaceRelationId, ++ database_oid, ++ SEPG_CLASS_DB_SCHEMA); ++} ++ ++sepgsql_sid_t ++sepgsqlGetDefaultTableSecid(Oid namespace_oid) ++{ ++ return defaultSecidWithSchema(RelationRelationId, ++ namespace_oid, ++ SEPG_CLASS_DB_TABLE); ++} ++ ++sepgsql_sid_t ++sepgsqlGetDefaultSequenceSecid(Oid namespace_oid) ++{ ++ return defaultSecidWithSchema(RelationRelationId, ++ namespace_oid, ++ SEPG_CLASS_DB_SEQUENCE); ++} ++ ++sepgsql_sid_t ++sepgsqlGetDefaultProcedureSecid(Oid namespace_oid) ++{ ++ return defaultSecidWithSchema(ProcedureRelationId, ++ namespace_oid, ++ SEPG_CLASS_DB_PROCEDURE); ++} ++ ++sepgsql_sid_t ++sepgsqlGetDefaultColumnSecid(Oid table_oid) ++{ ++ return defaultSecidWithTable(AttributeRelationId, ++ table_oid, ++ SEPG_CLASS_DB_COLUMN); ++} ++ ++sepgsql_sid_t ++sepgsqlGetDefaultTupleSecid(Oid table_oid) ++{ ++ return defaultSecidWithTable(table_oid, ++ table_oid, ++ SEPG_CLASS_DB_TUPLE); ++} ++ ++sepgsql_sid_t ++sepgsqlGetDefaultBlobSecid(Oid database_oid) ++{ ++ return defaultSecidWithDatabase(LargeObjectMetadataRelationId, ++ MyDatabaseId, ++ SEPG_CLASS_DB_BLOB); ++} ++ ++void ++sepgsqlSetDefaultSecid(Relation rel, HeapTuple tuple) ++{ ++ sepgsql_sid_t newSid; ++ Oid relOid = RelationGetRelid(rel); ++ Oid nspOid, tblOid; ++ char relkind; ++ ++ if (!HeapTupleHasSecid(tuple)) ++ return; ++ ++ /* initialize */ ++ newSid.relid = relOid; ++ newSid.secid = InvalidOid; ++ ++ switch (relOid) ++ { ++ case DatabaseRelationId: ++ /* should be never happen */ ++ elog(WARNING, "bug? pg_database tuple without security label"); ++ break; ++ ++ case NamespaceRelationId: ++ newSid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); ++ break; ++ ++ case RelationRelationId: ++ nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace; ++ relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind; ++ ++ switch (relkind) ++ { ++ case RELKIND_RELATION: ++ newSid = sepgsqlGetDefaultTableSecid(nspOid); ++ break; ++ ++ case RELKIND_SEQUENCE: ++ newSid = sepgsqlGetDefaultSequenceSecid(nspOid); ++ break; ++ ++ default: ++ newSid = sepgsqlGetDefaultTupleSecid(relOid); ++ break; ++ } ++ break; ++ ++ case ProcedureRelationId: ++ nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; ++ newSid = sepgsqlGetDefaultProcedureSecid(nspOid); ++ break; ++ ++ case AttributeRelationId: ++ tblOid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; ++ if (get_rel_relkind(tblOid) == RELKIND_RELATION) ++ newSid = sepgsqlGetDefaultColumnSecid(tblOid); ++ break; ++ ++ case LargeObjectMetadataRelationId: ++ newSid = sepgsqlGetDefaultBlobSecid(MyDatabaseId); ++ break; ++ ++ default: ++ newSid = sepgsqlGetDefaultTupleSecid(relOid); ++ break; ++ } ++ ++ HeapTupleSetSecid(tuple, newSid.secid); ++} ++ ++/* ++ * sepgsqlPostBootstrapingMode ++ * ++ * Assign initial security context ++ */ ++static void ++sepgsqlInitialLabeling(Oid relOid, char *seclabels[]) ++{ ++ Relation rel; ++ HeapScanDesc scan; ++ HeapTuple tuple; ++ HeapTuple newtup; ++ ++ rel = heap_open(relOid, RowExclusiveLock); ++ ++ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); ++ ++ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) ++ { ++ Oid secid = InvalidOid; ++ Oid attrelid; ++ char relkind; ++ ++ if (!HeapTupleHasSecid(tuple)) ++ continue; ++ ++ switch (relOid) ++ { ++ case DatabaseRelationId: ++ secid = securityRawSecLabelIn(relOid, seclabels[0]); ++ break; ++ ++ case NamespaceRelationId: ++ secid = securityRawSecLabelIn(relOid, seclabels[1]); ++ break; ++ ++ case RelationRelationId: ++ relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind; ++ switch (relkind) ++ { ++ case RELKIND_RELATION: ++ secid = securityRawSecLabelIn(relOid, seclabels[2]); ++ break; ++ case RELKIND_SEQUENCE: ++ secid = securityRawSecLabelIn(relOid, seclabels[3]); ++ break; ++ default: ++ secid = securityRawSecLabelIn(relOid, seclabels[6]); ++ break; ++ } ++ break; ++ ++ case AttributeRelationId: ++ attrelid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; ++ if (get_rel_relkind(attrelid) == RELKIND_RELATION) ++ secid = securityRawSecLabelIn(relOid, seclabels[5]); ++ break; ++ ++ case ProcedureRelationId: ++ secid = securityRawSecLabelIn(relOid, seclabels[4]); ++ break; ++ ++ case LargeObjectMetadataRelationId: ++ secid = securityRawSecLabelIn(relOid, seclabels[7]); ++ break; ++ ++ default: ++ secid = securityRawSecLabelIn(relOid, seclabels[6]); ++ break; ++ } ++ ++ /* ++ * Inplace update ++ */ ++ newtup = heap_copytuple(tuple); ++ ++ HeapTupleSetSecid(newtup, secid); ++ ++ heap_inplace_update(rel, newtup); ++ } ++ heap_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++void ++sepgsqlPostBootstrapingMode(void) ++{ ++ Form_pg_class classForm; ++ Relation rel; ++ ScanKeyData skey; ++ HeapScanDesc scan; ++ HeapTuple tuple; ++ char *scontext; ++ char *seclabels[8]; ++ ++ /* ++ * sepgsqlIsEnabled() is not available because it always returns ++ * false in bootstraping mode ++ */ ++ Assert(IsBootstrapProcessingMode()); ++ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED || ++ is_selinux_enabled() < 1) ++ return; ++ ++ /* ++ * Compute default initial security context ++ */ ++ if (getprevcon_raw(&scontext) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("could not obtain current context"))); ++ ++ seclabels[0] = sepgsqlComputeCreate(scontext, scontext, ++ SEPG_CLASS_DB_DATABASE); ++ seclabels[1] = sepgsqlComputeCreate(scontext, seclabels[0], ++ SEPG_CLASS_DB_SCHEMA); ++ seclabels[2] = sepgsqlComputeCreate(scontext, seclabels[1], ++ SEPG_CLASS_DB_TABLE); ++ seclabels[3] = sepgsqlComputeCreate(scontext, seclabels[1], ++ SEPG_CLASS_DB_SEQUENCE); ++ seclabels[4] = sepgsqlComputeCreate(scontext, seclabels[1], ++ SEPG_CLASS_DB_PROCEDURE); ++ seclabels[5] = sepgsqlComputeCreate(scontext, seclabels[2], ++ SEPG_CLASS_DB_COLUMN); ++ seclabels[6] = sepgsqlComputeCreate(scontext, seclabels[2], ++ SEPG_CLASS_DB_TUPLE); ++ seclabels[7] = sepgsqlComputeCreate(scontext, seclabels[0], ++ SEPG_CLASS_DB_BLOB); ++ /* ++ * Inplace update ++ */ ++ StartTransactionCommand(); ++ ++ rel = heap_open(RelationRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey, ++ Anum_pg_class_relkind, ++ BTEqualStrategyNumber, F_CHAREQ, ++ CharGetDatum(RELKIND_RELATION)); ++ ++ scan = heap_beginscan(rel, SnapshotNow, 1, &skey); ++ ++ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) ++ sepgsqlInitialLabeling(HeapTupleGetOid(tuple), seclabels); ++ ++ heap_endscan(scan); ++ ++ heap_close(rel, AccessShareLock); ++ ++ CommitTransactionCommand(); ++} ++ ++/* ++ * sepgsqlGetSysobjSecid ++ * ++ * It returns a pair of relid/secid for the given OID. ++ */ ++static sepgsql_sid_t ++getSysobjSecidDirect(Oid classOid, Oid indexOid, Oid objectId, uint16 *tclass) ++{ ++ sepgsql_sid_t sid; ++ Relation rel; ++ HeapTuple tup; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ ++ rel = heap_open(CastRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey, ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(objectId)); ++ ++ scan = systable_beginscan(rel, CastOidIndexId, true, ++ SnapshotNow, 1, &skey); ++ tup = systable_getnext(scan); ++ ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "system object lookup failed for oid %u on relation %u", ++ objectId, classOid); ++ ++ sid = sepgsqlGetTupleSecid(classOid, tup, tclass); ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, AccessShareLock); ++ ++ return sid; ++} ++ ++sepgsql_sid_t ++sepgsqlGetSysobjSecid(Oid classOid, Oid objectId, int32 objsubId, uint16 *tclass) ++{ ++ sepgsql_sid_t sid; ++ HeapTuple tup; ++ ++ switch (classOid) ++ { ++ case AccessMethodRelationId: ++ tup = SearchSysCache(AMOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for access method: %u", objectId); ++ break; ++ ++ case AccessMethodOperatorRelationId: ++ return getSysobjSecidDirect(AccessMethodOperatorRelationId, ++ AccessMethodOperatorOidIndexId, ++ objectId, tclass); ++ ++ case AccessMethodProcedureRelationId: ++ return getSysobjSecidDirect(AccessMethodProcedureRelationId, ++ AccessMethodProcedureOidIndexId, ++ objectId, tclass); ++ ++ case AuthIdRelationId: ++ tup = SearchSysCache(AUTHOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for role: %u", objectId); ++ break; ++ ++ case CastRelationId: ++ return getSysobjSecidDirect(CastRelationId, ++ CastOidIndexId, ++ objectId, tclass); ++ ++ case ConstraintRelationId: ++ tup = SearchSysCache(CONSTROID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for constraint: %u", objectId); ++ break; ++ ++ case ConversionRelationId: ++ tup = SearchSysCache(CONVOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for conversion: %u", objectId); ++ break; ++ ++ case DatabaseRelationId: ++ tup = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for database: %u", objectId); ++ break; ++ ++ case ForeignDataWrapperRelationId: ++ tup = SearchSysCache(FOREIGNDATAWRAPPEROID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for FDW: %u", objectId); ++ break; ++ ++ case ForeignServerRelationId: ++ tup = SearchSysCache(FOREIGNSERVEROID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for foreign server: %u", objectId); ++ break; ++ ++ case LanguageRelationId: ++ tup = SearchSysCache(LANGOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ break; ++ ++ case LargeObjectRelationId: ++ case LargeObjectMetadataRelationId: ++ { ++ Relation rel; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ ++ rel = heap_open(LargeObjectMetadataRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey, ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(objectId)); ++ ++ scan = systable_beginscan(rel, LargeObjectMetadataOidIndexId, ++ true, SnapshotNow, 1, &skey); ++ ++ tup = systable_getnext(scan); ++ ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "largeobject %u lookup failed", objectId); ++ ++ sid = sepgsqlGetTupleSecid(classOid, tup, tclass); ++ systable_endscan(scan); ++ ++ heap_close(rel, AccessShareLock); ++ } ++ return sid; ++ ++ case RelationRelationId: ++ if (objsubId != 0) ++ { ++ classOid = AttributeRelationId; ++ tup = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(objectId), ++ Int16GetDatum(objsubId), ++ 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for attribute %d of relation %u", ++ objsubId, objectId); ++ } ++ else ++ { ++ classOid = RelationRelationId; ++ tup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for relation %u", objectId); ++ } ++ break; ++ ++ case NamespaceRelationId: ++ tup = SearchSysCache(NAMESPACEOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for schema %u", objectId); ++ break; ++ ++ case OperatorClassRelationId: ++ tup = SearchSysCache(CLAOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for opclass %u", objectId); ++ break; ++ ++ case OperatorFamilyRelationId: ++ tup = SearchSysCache(OPFAMILYOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for opfamily %u", objectId); ++ break; ++ ++ case OperatorRelationId: ++ tup = SearchSysCache(OPEROID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for operator %u", objectId); ++ break; ++ ++ case ProcedureRelationId: ++ tup = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for procedure %u", objectId); ++ break; ++ ++ case RewriteRelationId: ++ return getSysobjSecidDirect(RewriteRelationId, ++ RewriteOidIndexId, ++ objectId, tclass); ++ ++ case TableSpaceRelationId: ++ return getSysobjSecidDirect(TableSpaceRelationId, ++ TablespaceOidIndexId, ++ objectId, tclass); ++ ++ case TriggerRelationId: ++ return getSysobjSecidDirect(TriggerRelationId, ++ TriggerOidIndexId, ++ objectId, tclass); ++ ++ case TSConfigRelationId: ++ tup = SearchSysCache(TSCONFIGOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for text search configuration %u", objectId); ++ break; ++ ++ case TSDictionaryRelationId: ++ tup = SearchSysCache(TSDICTOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for text search dictionary %u", objectId); ++ break; ++ ++ case TSParserRelationId: ++ tup = SearchSysCache(TSPARSEROID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for text search parser %u", objectId); ++ break; ++ ++ case TSTemplateRelationId: ++ tup = SearchSysCache(TSTEMPLATEOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for text search template %u", objectId); ++ break; ++ ++ case TypeRelationId: ++ tup = SearchSysCache(TYPEOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for type %u", objectId); ++ break; ++ ++ case UserMappingRelationId: ++ tup = SearchSysCache(USERMAPPINGOID, ++ ObjectIdGetDatum(objectId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tup)) ++ elog(ERROR, "cache lookup failed for user mapping %u", objectId); ++ break; ++ ++ default: ++ elog(ERROR, "unexpected class OID: %u", classOid); ++ tup = NULL; /* for compiler quiet */ ++ break; ++ } ++ ++ Assert(HeapTupleIsValid(tup)); ++ ++ sid = sepgsqlGetTupleSecid(classOid, tup, tclass); ++ ++ ReleaseSysCache(tup); ++ ++ return sid; ++} ++ ++/* ++ * sepgsqlGetTupleSecid ++ * ++ * It returns a pair of relid/secid for the given HeapTuple. ++ * A few system catalogs is handled as an attribute of other ++ * system objects. ++ * E.g) pg_attrdef is an attribute of a certain pg_attribute ++ */ ++sepgsql_sid_t ++sepgsqlGetTupleSecid(Oid tableOid, HeapTuple tuple, uint16 *tclass) ++{ ++ sepgsql_sid_t sid; ++ HeapTuple exttup; ++ Oid extid; ++ Oid extcls; ++ AttrNumber extsub; ++ ++ /* initialize (unlabeled security context) */ ++ sid.relid = tableOid; ++ sid.secid = InvalidOid; ++ if (tclass) ++ *tclass = SEPG_CLASS_DB_TUPLE; ++ ++ switch (tableOid) ++ { ++ case AggregateRelationId: ++ extid = ((Form_pg_aggregate) GETSTRUCT(tuple))->aggfnoid; ++ exttup = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(ProcedureRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case AccessMethodOperatorRelationId: ++ extid = ((Form_pg_amop) GETSTRUCT(tuple))->amopfamily; ++ exttup = SearchSysCache(OPFAMILYOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case AccessMethodProcedureRelationId: ++ extid = ((Form_pg_amproc) GETSTRUCT(tuple))->amprocfamily; ++ exttup = SearchSysCache(OPFAMILYOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case AttrDefaultRelationId: ++ extid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid; ++ extsub = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum; ++ exttup = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(extid), ++ Int16GetDatum(extsub), ++ 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(AttributeRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case AttributeRelationId: ++ extid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; ++ exttup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ char relkind = ((Form_pg_class) GETSTRUCT(exttup))->relkind; ++ ++ if (relkind == RELKIND_RELATION) ++ { ++ if (tclass) ++ *tclass = SEPG_CLASS_DB_COLUMN; ++ sid.secid = HeapTupleGetSecid(tuple); ++ } ++ else ++ sid = sepgsqlGetTupleSecid(RelationRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case AuthMemRelationId: ++ extid = ((Form_pg_auth_members) GETSTRUCT(tuple))->roleid; ++ exttup = SearchSysCache(AUTHOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(AuthIdRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case ConstraintRelationId: ++ /* CHECK constraint is an attribute of the relation */ ++ extid = ((Form_pg_constraint) GETSTRUCT(tuple))->conrelid; ++ if (OidIsValid(extid)) ++ { ++ exttup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(RelationRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ } ++ /* DOMAIN constraint is an attribute of the domain type */ ++ extid = ((Form_pg_constraint) GETSTRUCT(tuple))->contypid; ++ if (OidIsValid(extid)) ++ { ++ sid.relid = TypeRelationId; ++ exttup = SearchSysCache(TYPEOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(TypeRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ } ++ /* Database's context for global assertion */ ++ exttup = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(DatabaseRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case DatabaseRelationId: ++ sid.secid = HeapTupleGetSecid(tuple); ++ if (tclass) ++ *tclass = SEPG_CLASS_DB_DATABASE; ++ break; ++ ++ case DescriptionRelationId: ++ /* recursive call */ ++ extid = ((Form_pg_description) GETSTRUCT(tuple))->objoid; ++ extcls = ((Form_pg_description) GETSTRUCT(tuple))->classoid; ++ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass); ++ ++ case EnumRelationId: ++ extid = ((Form_pg_enum) GETSTRUCT(tuple))->enumtypid; ++ exttup = SearchSysCache(TYPEOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(TypeRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case IndexRelationId: ++ extid = ((Form_pg_index) GETSTRUCT(tuple))->indrelid; ++ exttup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(RelationRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case InheritsRelationId: ++ extid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid; ++ exttup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(RelationRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case LargeObjectRelationId: ++ extid = ((Form_pg_largeobject) GETSTRUCT(tuple))->loid; ++ extcls = LargeObjectMetadataRelationId; ++ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass); ++ ++ case LargeObjectMetadataRelationId: ++ sid.secid = HeapTupleGetSecid(tuple); ++ if (tclass) ++ *tclass = SEPG_CLASS_DB_BLOB; ++ break; ++ ++ case NamespaceRelationId: ++ sid.secid = HeapTupleGetSecid(tuple); ++ if (tclass) ++ *tclass = SEPG_CLASS_DB_SCHEMA; ++ break; ++ ++ case ProcedureRelationId: ++ sid.secid = HeapTupleGetSecid(tuple); ++ if (tclass) ++ *tclass = SEPG_CLASS_DB_PROCEDURE; ++ break; ++ ++ case RelationRelationId: ++ sid.secid = HeapTupleGetSecid(tuple); ++ if (tclass) ++ { ++ char relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind; ++ ++ switch (relkind) ++ { ++ case RELKIND_RELATION: ++ *tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ ++ case RELKIND_SEQUENCE: ++ *tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ ++ default: ++ *tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ } ++ } ++ break; ++ ++ case RewriteRelationId: ++ extid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class; ++ exttup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(RelationRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case SharedDescriptionRelationId: ++ /* recursive invocation */ ++ extid = ((Form_pg_shdescription) GETSTRUCT(tuple))->objoid; ++ extcls = ((Form_pg_shdescription) GETSTRUCT(tuple))->classoid; ++ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass); ++ ++ case StatisticRelationId: ++ extid = ((Form_pg_statistic) GETSTRUCT(tuple))->starelid; ++ extsub = ((Form_pg_statistic) GETSTRUCT(tuple))->staattnum; ++ exttup = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(extid), ++ Int16GetDatum(extsub), ++ 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(AttributeRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case TriggerRelationId: ++ extid = ((Form_pg_trigger) GETSTRUCT(tuple))->tgrelid; ++ exttup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(RelationRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ case TSConfigMapRelationId: ++ extid = ((Form_pg_ts_config_map) GETSTRUCT(tuple))->mapcfg; ++ exttup = SearchSysCache(TSCONFIGOID, ++ ObjectIdGetDatum(extid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(exttup)) ++ { ++ sid = sepgsqlGetTupleSecid(TSConfigRelationId, ++ exttup, tclass); ++ ReleaseSysCache(exttup); ++ } ++ break; ++ ++ default: ++ /* No external lookups (normal case) */ ++ sid.secid = HeapTupleGetSecid(tuple); ++ break; ++ } ++ ++ return sid; ++} ++ ++/* ++ * sepgsqlRawSecLabelIn ++ * correctness checks for the given security context ++ */ ++char * ++sepgsqlRawSecLabelIn(char *seclabel) ++{ ++ if (!sepgsqlIsEnabled()) ++ return seclabel; ++ ++ if (!seclabel || security_check_context_raw(seclabel) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_SECURITY_LABEL), ++ errmsg("Invalid security context: \"%s\"", seclabel))); ++ ++ return seclabel; ++} ++ ++/* ++ * sepgsqlRawSecLabelOut ++ * correctness checks for the given security context, ++ * and replace it if invalid security context ++ */ ++char * ++sepgsqlRawSecLabelOut(char *seclabel) ++{ ++ if (!sepgsqlIsEnabled()) ++ return seclabel; ++ ++ if (!seclabel || security_check_context_raw(seclabel) < 0) ++ { ++ security_context_t unlabeledcon; ++ ++ if (security_get_initial_context_raw("unlabeled", ++ &unlabeledcon) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("Unabled to get unlabeled security context"))); ++ PG_TRY(); ++ { ++ seclabel = pstrdup(unlabeledcon); ++ } ++ PG_CATCH(); ++ { ++ freecon(unlabeledcon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(unlabeledcon); ++ } ++ return seclabel; ++} ++ ++/* ++ * sepgsqlTransSecLabelIn ++ * sepgsqlTransSecLabelOut ++ * translation between human-readable and raw format ++ */ ++char * ++sepgsqlTransSecLabelIn(char *seclabel) ++{ ++ security_context_t rawlabel; ++ security_context_t result; ++ ++ if (!sepgsqlIsEnabled() || ++ !sepostgresql_mcstrans) ++ return seclabel; ++ ++ if (selinux_trans_to_raw_context(seclabel, &rawlabel) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux: failed to translate \"%s\"", seclabel))); ++ PG_TRY(); ++ { ++ result = pstrdup(rawlabel); ++ } ++ PG_CATCH(); ++ { ++ freecon(rawlabel); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(rawlabel); ++ ++ return result; ++} ++ ++char * ++sepgsqlTransSecLabelOut(char *seclabel) ++{ ++ security_context_t translabel; ++ security_context_t result; ++ ++ if (!sepgsqlIsEnabled() || ++ !sepostgresql_mcstrans) ++ return seclabel; ++ ++ if (selinux_raw_to_trans_context(seclabel, &translabel) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux: failed to translate \"%s\"", seclabel))); ++ PG_TRY(); ++ { ++ result = pstrdup(translabel); ++ } ++ PG_CATCH(); ++ { ++ freecon(translabel); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(translabel); ++ ++ return result; ++} ++ ++char * ++sepgsqlSysattSecLabelOut(Oid relid, HeapTuple tuple) ++{ ++ sepgsql_sid_t sid; ++ ++ sid = sepgsqlGetTupleSecid(relid, tuple, NULL); ++ ++ return securityTransSecLabelOut(sid.relid, sid.secid); ++} +diff --git a/src/backend/security/sepgsql/misc.c b/src/backend/security/sepgsql/misc.c +new file mode 100644 +index 0000000..2f7c466 +--- /dev/null ++++ b/src/backend/security/sepgsql/misc.c +@@ -0,0 +1,214 @@ ++/* ++ * src/backend/security/sepgsql/misc.c ++ * Miscellaneous facilities in SE-PostgreSQL ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++#include "libpq/libpq-be.h" ++#include "miscadmin.h" ++#include "security/sepgsql.h" ++#include "utils/builtins.h" ++ ++/* ++ * SE-PostgreSQL specific functions ++ */ ++Datum ++sepgsql_getcon(PG_FUNCTION_ARGS) ++{ ++ security_context_t context; ++ ++ if (!sepgsqlIsEnabled()) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SELinux: disabled now"))); ++ ++ context = sepgsqlGetClientLabel(); ++ context = sepgsqlTransSecLabelOut(context); ++ return CStringGetTextDatum(context); ++} ++ ++Datum ++sepgsql_server_getcon(PG_FUNCTION_ARGS) ++{ ++ char *context; ++ ++ if (!sepgsqlIsEnabled()) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SELinux: disabled now"))); ++ ++ context = sepgsqlGetServerLabel(); ++ context = sepgsqlTransSecLabelOut(context); ++ ++ return CStringGetTextDatum(context); ++} ++ ++/* ++ * sepgsql_(get|set)_(user|role|type|range) ++ * get/set a component of security context. ++ */ ++static void ++parse_security_context(security_context_t context, ++ char **user, char **role, char **type, char **range) ++{ ++ security_context_t raw_context; ++ char *tok; ++ ++ if (!sepgsqlIsEnabled()) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SELinux: disabled now"))); ++ ++ if (selinux_trans_to_raw_context(context, &raw_context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("could not translate mls label: %s", context))); ++ ++ PG_TRY(); ++ { ++ tok = strtok(raw_context, ":"); ++ if (user) ++ *user = (!tok ? NULL : pstrdup(tok)); ++ ++ tok = strtok(NULL, ":"); ++ if (role) ++ *role = (!tok ? NULL : pstrdup(tok)); ++ ++ tok = strtok(NULL, ":"); ++ if (type) ++ *type = (!tok ? NULL : pstrdup(tok)); ++ ++ tok = strtok(NULL, "\0"); ++ if (range) ++ *range = (!tok ? NULL : pstrdup(tok)); ++ } ++ PG_CATCH(); ++ { ++ freecon(raw_context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(raw_context); ++} ++ ++Datum ++sepgsql_get_user(PG_FUNCTION_ARGS) ++{ ++ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); ++ char *user; ++ ++ parse_security_context(context, &user, NULL, NULL, NULL); ++ if (!user) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_SECURITY_LABEL), ++ errmsg("could not extract user of \"%s\"", context))); ++ ++ PG_RETURN_TEXT_P(CStringGetTextDatum(user)); ++} ++ ++Datum ++sepgsql_get_role(PG_FUNCTION_ARGS) ++{ ++ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); ++ char *role; ++ ++ parse_security_context(context, NULL, &role, NULL, NULL); ++ if (!role) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_SECURITY_LABEL), ++ errmsg("could not extract role of \"%s\"", context))); ++ ++ PG_RETURN_TEXT_P(CStringGetTextDatum(role)); ++} ++ ++Datum ++sepgsql_get_type(PG_FUNCTION_ARGS) ++{ ++ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); ++ char *type; ++ ++ parse_security_context(context, NULL, NULL, &type, NULL); ++ if (!type) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_SECURITY_LABEL), ++ errmsg("could not extract type of \"%s\"", context))); ++ ++ PG_RETURN_TEXT_P(CStringGetTextDatum(type)); ++} ++ ++Datum ++sepgsql_get_range(PG_FUNCTION_ARGS) ++{ ++ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); ++ char *range; ++ ++ parse_security_context(context, NULL, NULL, NULL, &range); ++ if (!range) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_SECURITY_LABEL), ++ errmsg("could not extract range of \"%s\"", context))); ++ ++ PG_RETURN_TEXT_P(CStringGetTextDatum(range)); ++} ++ ++static Datum ++sepgsql_set_common(char *context, ++ char *user, char *role, char *type, char *range) ++{ ++ StringInfoData newcon; ++ ++ parse_security_context(context, ++ !user ? &user : NULL, ++ !role ? &role : NULL, ++ !type ? &type : NULL, ++ !range ? &range : NULL); ++ if (!user || !role || !type) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_SECURITY_LABEL), ++ errmsg("invalid security context: \"%s\"", context))); ++ ++ initStringInfo(&newcon); ++ appendStringInfo(&newcon, "%s:%s:%s", user, role, type); ++ if (range) ++ appendStringInfo(&newcon, ":%s", range); ++ ++ return CStringGetTextDatum(sepgsqlTransSecLabelOut(newcon.data)); ++} ++ ++Datum ++sepgsql_set_user(PG_FUNCTION_ARGS) ++{ ++ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); ++ char *user = TextDatumGetCString(PG_GETARG_TEXT_P(1)); ++ ++ return sepgsql_set_common(context, user, NULL, NULL, NULL); ++} ++ ++Datum ++sepgsql_set_role(PG_FUNCTION_ARGS) ++{ ++ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); ++ char *role = TextDatumGetCString(PG_GETARG_TEXT_P(1)); ++ ++ return sepgsql_set_common(context, NULL, role, NULL, NULL); ++} ++ ++Datum ++sepgsql_set_type(PG_FUNCTION_ARGS) ++{ ++ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); ++ char *type = TextDatumGetCString(PG_GETARG_TEXT_P(1)); ++ ++ return sepgsql_set_common(context, NULL, NULL, type, NULL); ++} ++ ++Datum ++sepgsql_set_range(PG_FUNCTION_ARGS) ++{ ++ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); ++ char *range = TextDatumGetCString(PG_GETARG_TEXT_P(1)); ++ ++ return sepgsql_set_common(context, NULL, NULL, NULL, range); ++} +diff --git a/src/backend/security/sepgsql/perms.c b/src/backend/security/sepgsql/perms.c +new file mode 100644 +index 0000000..5943f40 +--- /dev/null ++++ b/src/backend/security/sepgsql/perms.c +@@ -0,0 +1,597 @@ ++/* ++ * src/backend/utils/sepgsql/perms.c ++ * SE-PostgreSQL permission checks ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_database.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_type.h" ++#include "miscadmin.h" ++#include "security/sepgsql.h" ++#include "utils/lsyscache.h" ++ ++/* ++ * Dynamic object class/permissions mapping ++ * ++ * SELinux exports the list of object classes and permissions at ++ * /selinux/class. The libselinux provides an interface to translate ++ * between their names and codes. ++ */ ++static struct ++{ ++ const char *class_name; ++ security_class_t class_code; ++ struct ++ { ++ const char *perm_name; ++ access_vector_t perm_code; ++ } av[sizeof(access_vector_t) * 8]; ++} selinux_catalog[] = { ++ { ++ "process", SEPG_CLASS_PROCESS, ++ { ++ {"translation", SEPG_PROCESS__TRANSITION }, ++ {NULL, 0} ++ } ++ }, ++ { ++ "file", SEPG_CLASS_FILE, ++ { ++ {"read", SEPG_FILE__READ }, ++ {"write", SEPG_FILE__WRITE }, ++ {"create", SEPG_FILE__CREATE }, ++ {"getattr", SEPG_FILE__GETATTR }, ++ {NULL, 0} ++ } ++ }, ++ { ++ "dir", SEPG_CLASS_DIR, ++ { ++ {"read", SEPG_DIR__READ }, ++ {"write", SEPG_DIR__WRITE }, ++ {"create", SEPG_DIR__CREATE }, ++ {"getattr", SEPG_DIR__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "lnk_file", SEPG_CLASS_LNK_FILE, ++ { ++ {"read", SEPG_LNK_FILE__READ }, ++ {"write", SEPG_LNK_FILE__WRITE }, ++ {"create", SEPG_LNK_FILE__CREATE }, ++ {"getattr", SEPG_LNK_FILE__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "chr_file", SEPG_CLASS_CHR_FILE, ++ { ++ {"read", SEPG_CHR_FILE__READ }, ++ {"write", SEPG_CHR_FILE__WRITE }, ++ {"create", SEPG_CHR_FILE__CREATE }, ++ {"getattr", SEPG_CHR_FILE__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "blk_file", SEPG_CLASS_BLK_FILE, ++ { ++ {"read", SEPG_BLK_FILE__READ }, ++ {"write", SEPG_BLK_FILE__WRITE }, ++ {"create", SEPG_BLK_FILE__CREATE }, ++ {"getattr", SEPG_BLK_FILE__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "sock_file", SEPG_CLASS_SOCK_FILE, ++ { ++ {"read", SEPG_SOCK_FILE__READ }, ++ {"write", SEPG_SOCK_FILE__WRITE }, ++ {"create", SEPG_SOCK_FILE__CREATE }, ++ {"getattr", SEPG_SOCK_FILE__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "fifo_file", SEPG_CLASS_FIFO_FILE, ++ { ++ {"read", SEPG_FIFO_FILE__READ }, ++ {"write", SEPG_FIFO_FILE__WRITE }, ++ {"create", SEPG_FIFO_FILE__CREATE }, ++ {"getattr", SEPG_FIFO_FILE__GETATTR }, ++ {NULL, 0UL } ++ } ++ }, ++ { ++ "db_database", SEPG_CLASS_DB_DATABASE, ++ { ++ { "create", SEPG_DB_DATABASE__CREATE }, ++ { "drop", SEPG_DB_DATABASE__DROP }, ++ { "getattr", SEPG_DB_DATABASE__GETATTR }, ++ { "setattr", SEPG_DB_DATABASE__SETATTR }, ++ { "relabelfrom", SEPG_DB_DATABASE__RELABELFROM }, ++ { "relabelto", SEPG_DB_DATABASE__RELABELTO }, ++ { "access", SEPG_DB_DATABASE__ACCESS }, ++ { "install_module", SEPG_DB_DATABASE__INSTALL_MODULE }, ++ { "load_module", SEPG_DB_DATABASE__LOAD_MODULE }, ++ { "superuser", SEPG_DB_DATABASE__SUPERUSER }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_schema", SEPG_CLASS_DB_SCHEMA, ++ { ++ { "create", SEPG_DB_SCHEMA__CREATE }, ++ { "drop", SEPG_DB_SCHEMA__DROP }, ++ { "getattr", SEPG_DB_SCHEMA__GETATTR }, ++ { "setattr", SEPG_DB_SCHEMA__SETATTR }, ++ { "relabelfrom", SEPG_DB_SCHEMA__RELABELFROM }, ++ { "relabelto", SEPG_DB_SCHEMA__RELABELTO }, ++ { "search", SEPG_DB_SCHEMA__SEARCH }, ++ { "add_name", SEPG_DB_SCHEMA__ADD_NAME }, ++ { "remove_name", SEPG_DB_SCHEMA__REMOVE_NAME }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_table", SEPG_CLASS_DB_TABLE, ++ { ++ { "create", SEPG_DB_TABLE__CREATE }, ++ { "drop", SEPG_DB_TABLE__DROP }, ++ { "getattr", SEPG_DB_TABLE__GETATTR }, ++ { "setattr", SEPG_DB_TABLE__SETATTR }, ++ { "relabelfrom", SEPG_DB_TABLE__RELABELFROM }, ++ { "relabelto", SEPG_DB_TABLE__RELABELTO }, ++ { "select", SEPG_DB_TABLE__SELECT }, ++ { "update", SEPG_DB_TABLE__UPDATE }, ++ { "insert", SEPG_DB_TABLE__INSERT }, ++ { "delete", SEPG_DB_TABLE__DELETE }, ++ { "lock", SEPG_DB_TABLE__LOCK }, ++ { "reference", SEPG_DB_TABLE__REFERENCE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_sequence", SEPG_CLASS_DB_SEQUENCE, ++ { ++ { "create", SEPG_DB_SEQUENCE__CREATE }, ++ { "drop", SEPG_DB_SEQUENCE__DROP }, ++ { "getattr", SEPG_DB_SEQUENCE__GETATTR }, ++ { "setattr", SEPG_DB_SEQUENCE__SETATTR }, ++ { "relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM }, ++ { "relabelto", SEPG_DB_SEQUENCE__RELABELTO }, ++ { "get_value", SEPG_DB_SEQUENCE__GET_VALUE }, ++ { "next_value", SEPG_DB_SEQUENCE__NEXT_VALUE }, ++ { "set_value", SEPG_DB_SEQUENCE__SET_VALUE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_procedure", SEPG_CLASS_DB_PROCEDURE, ++ { ++ { "create", SEPG_DB_PROCEDURE__CREATE }, ++ { "drop", SEPG_DB_PROCEDURE__DROP }, ++ { "getattr", SEPG_DB_PROCEDURE__GETATTR }, ++ { "setattr", SEPG_DB_PROCEDURE__SETATTR }, ++ { "relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM }, ++ { "relabelto", SEPG_DB_PROCEDURE__RELABELTO }, ++ { "execute", SEPG_DB_PROCEDURE__EXECUTE }, ++ { "entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT }, ++ { "install", SEPG_DB_PROCEDURE__INSTALL }, ++ { "untrusted", SEPG_DB_PROCEDURE__UNTRUSTED }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_column", SEPG_CLASS_DB_COLUMN, ++ { ++ { "create", SEPG_DB_COLUMN__CREATE }, ++ { "drop", SEPG_DB_COLUMN__DROP }, ++ { "getattr", SEPG_DB_COLUMN__GETATTR }, ++ { "setattr", SEPG_DB_COLUMN__SETATTR }, ++ { "relabelfrom", SEPG_DB_COLUMN__RELABELFROM }, ++ { "relabelto", SEPG_DB_COLUMN__RELABELTO }, ++ { "select", SEPG_DB_COLUMN__SELECT }, ++ { "update", SEPG_DB_COLUMN__UPDATE }, ++ { "insert", SEPG_DB_COLUMN__INSERT }, ++ { "reference", SEPG_DB_COLUMN__REFERENCE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_tuple", SEPG_CLASS_DB_TUPLE, ++ { ++ { "relabelfrom", SEPG_DB_TUPLE__RELABELFROM }, ++ { "relabelto", SEPG_DB_TUPLE__RELABELTO }, ++ { "select", SEPG_DB_TUPLE__SELECT }, ++ { "update", SEPG_DB_TUPLE__UPDATE }, ++ { "insert", SEPG_DB_TUPLE__INSERT }, ++ { "delete", SEPG_DB_TUPLE__DELETE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_blob", SEPG_CLASS_DB_BLOB, ++ { ++ { "create", SEPG_DB_BLOB__CREATE }, ++ { "drop", SEPG_DB_BLOB__DROP }, ++ { "getattr", SEPG_DB_BLOB__GETATTR }, ++ { "setattr", SEPG_DB_BLOB__SETATTR }, ++ { "relabelfrom", SEPG_DB_BLOB__RELABELFROM }, ++ { "relabelto", SEPG_DB_BLOB__RELABELTO }, ++ { "read", SEPG_DB_BLOB__READ }, ++ { "write", SEPG_DB_BLOB__WRITE }, ++ { "import", SEPG_DB_BLOB__IMPORT }, ++ { "export", SEPG_DB_BLOB__EXPORT }, ++ { NULL, 0UL }, ++ } ++ } ++}; ++ ++/* ++ * sepgsqlTransToExternalClass ++ * It translate the given class code (defined as SEPGCLASS_(class)) into ++ * external code which is necessary to communicate in-kernel SELinux ++ */ ++extern security_class_t ++sepgsqlTransToExternalClass(uint16 tclass) ++{ ++ Assert(tclass < SEPG_CLASS_MAX); ++ ++ return string_to_security_class(selinux_catalog[tclass].class_name); ++} ++ ++/* ++ * sepgsqlTransToInternalPerms ++ * It translate the given permission masks into internal representation ++ * defined as SEPG_(class)_(permission). ++ */ ++extern void ++sepgsqlTransToInternalPerms(security_class_t tclass, struct av_decision *avd) ++{ ++ security_class_t tclass_ex; ++ struct av_decision i_avd; ++ int i, deny_unknown; ++ ++ Assert(tclass < SEPG_CLASS_MAX); ++ ++ memset(&i_avd, 0, sizeof(struct av_decision)); ++ ++ deny_unknown = security_deny_unknown(); ++ ++ tclass_ex = sepgsqlTransToExternalClass(tclass); ++ for (i=0; selinux_catalog[tclass].av[i].perm_name; i++) ++ { ++ const char *perm_name = selinux_catalog[tclass].av[i].perm_name; ++ access_vector_t perm_code = selinux_catalog[tclass].av[i].perm_code; ++ access_vector_t perm_code_ex; ++ ++ perm_code_ex = string_to_av_perm(tclass_ex, perm_name); ++ if (!perm_code_ex) ++ { ++ /* fill up undefined permission */ ++ if (!deny_unknown) ++ i_avd.allowed |= perm_code; ++ i_avd.decided |= perm_code; ++ i_avd.auditdeny |= perm_code; ++ continue; ++ } ++ ++ if (avd->allowed & perm_code_ex) ++ i_avd.allowed |= perm_code; ++ if (avd->decided & perm_code_ex) ++ i_avd.decided |= perm_code; ++ if (avd->auditallow & perm_code_ex) ++ i_avd.auditallow |= perm_code; ++ if (avd->auditdeny & perm_code_ex) ++ i_avd.auditdeny |= perm_code; ++ } ++ ++ avd->allowed = i_avd.allowed; ++ avd->decided = i_avd.decided; ++ avd->auditallow = i_avd.auditallow; ++ avd->auditdeny = i_avd.auditdeny; ++} ++ ++/* ++ * sepgsqlGetClassString ++ * sepgsqlGetPermissionString ++ * It returns text representation of object classes/permissions ++ */ ++const char * ++sepgsqlGetClassString(uint16 tclass) ++{ ++ Assert(tclass < SEPG_CLASS_MAX); ++ ++ return selinux_catalog[tclass].class_name; ++} ++ ++const char * ++sepgsqlGetPermString(uint16 tclass, uint32 permission) ++{ ++ int i; ++ ++ Assert(tclass < SEPG_CLASS_MAX); ++ ++ for (i=0; selinux_catalog[tclass].av[i].perm_name; i++) ++ { ++ if (selinux_catalog[tclass].av[i].perm_code == permission) ++ return selinux_catalog[tclass].av[i].perm_name; ++ } ++ return NULL; ++} ++ ++#if 0 ++ ++/* ++ * sepgsqlFileObjectClass ++ * ++ * It returns proper object class of filesystem object already opened. ++ * It is necessary to check privileges voluntarily. ++ */ ++uint16 ++sepgsqlFileObjectClass(int fdesc) ++{ ++ struct stat stbuf; ++ ++ if (fstat(fdesc, &stbuf) != 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not stat file descriptor: %d", fdesc))); ++ ++ if (S_ISDIR(stbuf.st_mode)) ++ return SEPG_CLASS_DIR; ++ else if (S_ISCHR(stbuf.st_mode)) ++ return SEPG_CLASS_CHR_FILE; ++ else if (S_ISBLK(stbuf.st_mode)) ++ return SEPG_CLASS_BLK_FILE; ++ else if (S_ISFIFO(stbuf.st_mode)) ++ return SEPG_CLASS_FIFO_FILE; ++ else if (S_ISLNK(stbuf.st_mode)) ++ return SEPG_CLASS_LNK_FILE; ++ else if (S_ISSOCK(stbuf.st_mode)) ++ return SEPG_CLASS_SOCK_FILE; ++ ++ return SEPG_CLASS_FILE; ++} ++ ++/* ++ * sepgsqlTupleObjectClass ++ * ++ * It returns correct object class of given tuple ++ */ ++uint16 ++sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple) ++{ ++ Form_pg_class clsForm; ++ Form_pg_attribute attForm; ++ ++ switch (relid) ++ { ++ case DatabaseRelationId: ++ return SEPG_CLASS_DB_DATABASE; ++ ++ case NamespaceRelationId: ++ return SEPG_CLASS_DB_SCHEMA; ++ ++ case RelationRelationId: ++ clsForm = (Form_pg_class) GETSTRUCT(tuple); ++ if (clsForm->relkind == RELKIND_RELATION) ++ return SEPG_CLASS_DB_TABLE; ++ if (clsForm->relkind == RELKIND_SEQUENCE) ++ return SEPG_CLASS_DB_SEQUENCE; ++ break; ++ ++ case AttributeRelationId: ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ if (IsBootstrapProcessingMode() && ++ (attForm->attrelid == TypeRelationId || ++ attForm->attrelid == ProcedureRelationId || ++ attForm->attrelid == AttributeRelationId || ++ attForm->attrelid == RelationRelationId)) ++ return SEPG_CLASS_DB_COLUMN; ++ ++ if (get_rel_relkind(attForm->attrelid) == RELKIND_RELATION) ++ return SEPG_CLASS_DB_COLUMN; ++ break; ++ ++ case ProcedureRelationId: ++ return SEPG_CLASS_DB_PROCEDURE; ++ ++ case LargeObjectRelationId: ++ return SEPG_CLASS_DB_BLOB; ++ } ++ return SEPG_CLASS_DB_TUPLE; ++} ++ ++/* ++ * sepgsqlTupleNamespace ++ * ++ * It returns an OID of the namespace, if the given system object is ++ * deployed under a certain namespace. ++ */ ++Oid ++sepgsqlTupleNamespace(Oid relOid, HeapTuple tuple) ++{ ++ Oid nspOid; ++ ++ switch (relOid) ++ { ++ case RelationRelationId: ++ nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace; ++ break; ++ ++ case ConstraintRelationId: ++ nspOid = ((Form_pg_constraint) GETSTRUCT(tuple))->connamespace; ++ break; ++ ++ case ConversionRelationId: ++ nspOid = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace; ++ break; ++ ++ case OperatorClassRelationId: ++ nspOid = ((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace; ++ break; ++ ++ case OperatorRelationId: ++ nspOid = ((Form_pg_operator) GETSTRUCT(tuple))->oprnamespace; ++ break; ++ ++ case OperatorFamilyRelationId: ++ nspOid = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace; ++ break; ++ ++ case ProcedureRelationId: ++ nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; ++ break; ++ ++ case TSConfigRelationId: ++ nspOid = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace; ++ break; ++ ++ case TSDictionaryRelationId: ++ nspOid = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace; ++ break; ++ ++ case TSParserRelationId: ++ nspOid = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace; ++ break; ++ ++ case TSTemplateRelationId: ++ nspOid = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace; ++ break; ++ ++ default: ++ /* no specific namespace */ ++ nspOid = InvalidOid; ++ break; ++ } ++ ++ return nspOid; ++} ++ ++/* ++ * sepgsqlTupleAuditName ++ * ++ * It returns an OID of the namespace, if the given system object is ++ * deployed under a certain namespace. ++ */ ++void ++sepgsqlTupleAuditName(Oid relid, HeapTuple tuple, char *auname_buf) ++{ ++ char *name; ++ Oid extid; ++ ++ switch (relid) ++ { ++ case AccessMethodRelationId: ++ name = NameStr(((Form_pg_am) GETSTRUCT(tuple))->amname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case AttributeRelationId: ++ name = NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname); ++ extid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; ++ sprintf(audit_name, "%s.%s", name, extid); ++ return; ++ ++ case AuthIdRelationId: ++ name = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case ConversionRelationId: ++ name = NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case DatabaseRelationId: ++ name = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case ForeignDataWrapperRelationId: ++ name = NameStr(((Form_pg_foreign_data_wrapper) GETSTRUCT(tuple))->fdwname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case ForeignServerRelationId: ++ name = NameStr(((Form_pg_foreign_server) GETSTRUCT(tuple))->srvname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case LanguageRelationId: ++ name = NameStr(((Form_pg_language) GETSTRUCT(tuple))->lanname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case NamespaceRelationId: ++ name = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case OperatorClassRelationId: ++ name = NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case OperatorRelationId: ++ name = NameStr(((Form_pg_operator) GETSTRUCT(tuple))->oprname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case OperatorFamilyRelationId: ++ name = NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case ProcedureRelationId: ++ name = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case RelationRelationId: ++ name = NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TableSpaceRelationId: ++ name = NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TSConfigRelationId: ++ name = NameStr(((Form_pg_ts_config) GETSTRUCT(tuple))->cfgname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TSDictionaryRelationId: ++ name = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TSParserRelationId: ++ name = NameStr(((Form_pg_ts_parser) GETSTRUCT(tuple))->prsname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TSTemplateRelationId: ++ name = NameStr(((Form_pg_templace) GETSTRUCT(tuple))->tmplname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ default: ++ /* no auditable name */ ++ auname_buf[0] = '\0'; ++ break; ++ } ++} ++#endif +diff --git a/src/backend/security/sepgsql/policy/Makefile b/src/backend/security/sepgsql/policy/Makefile +new file mode 100644 +index 0000000..fc71b0c +--- /dev/null ++++ b/src/backend/security/sepgsql/policy/Makefile +@@ -0,0 +1,28 @@ ++# ++# Makefile for SE-PostgreSQL security policy module ++# ++top_builddir = ../../../../.. ++include $(top_builddir)/src/Makefile.global ++ ++POLICY_BASEDIR := $(DESTDIR)/usr/share/selinux ++POLICY_MAKEFILE := $(POLICY_BASEDIR)/devel/Makefile ++POLICY_INSTDIR := $(POLICY_BASEDIR)/packages ++PREFIX_RULE := "s/%%__prefix__%%/$(shell echo $(prefix)|sed 's/\//\\\//g')/g" ++BINDIR_RULE := "s/%%__bindir__%%/$(shell echo $(bindir)|sed 's/\//\\\//g')/g" ++LIBDIR_RULE := "s/%%__libdir__%%/$(shell echo $(pkglibdir)|sed 's/\//\\\//g')/g" ++ ++all: sepostgresql-devel.pp ++ ++install: all ++ test -d $(POLICY_INSTDIR) || mkdir -p $(POLICY_INSTDIR) ++ install -p -m 0644 sepostgresql-devel.pp $(POLICY_INSTDIR) ++ ++sepostgresql-devel.pp: sepostgresql-devel.te sepostgresql-devel.fc ++ $(MAKE) -f $(POLICY_MAKEFILE) ++ ++sepostgresql-devel.fc: sepostgresql-devel.fc.template ++ cat $< | sed -e $(PREFIX_RULE) -e $(BINDIR_RULE) -e $(LIBDIR_RULE) > $@ ++ ++clean: ++ $(MAKE) -f $(POLICY_MAKEFILE) clean ++ rm -f *.fc +diff --git a/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template b/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template +new file mode 100644 +index 0000000..380ada4 +--- /dev/null ++++ b/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template +@@ -0,0 +1,12 @@ ++# ++# SE-PostgreSQL install path ++# ++%%__prefix__%%(/.*)? -- gen_context(system_u:object_r:usr_t,s0) ++ ++%%__bindir__%%/(se)?postgres -- gen_context(system_u:object_r:postgresql_exec_t,s0) ++%%__bindir__%%/(se)?pg_ctl -- gen_context(system_u:object_r:initrc_exec_t,s0) ++%%__bindir__%%/initdb(\.sepgsql)? -- gen_context(system_u:object_r:postgresql_exec_t,s0) ++%%__bindir__%%(/.*)? -- gen_context(system_u:object_r:bin_t,s0) ++ ++%%__libdir__%%(/.*)? -- gen_context(system_u:object_r:lib_t,s0) ++ +diff --git a/src/backend/security/sepgsql/policy/sepostgresql-devel.te b/src/backend/security/sepgsql/policy/sepostgresql-devel.te +new file mode 100644 +index 0000000..4b960a8 +--- /dev/null ++++ b/src/backend/security/sepgsql/policy/sepostgresql-devel.te +@@ -0,0 +1,123 @@ ++policy_module(sepostgresql-devel, 3.29) ++ ++gen_require(` ++ class db_database all_db_database_perms; ++ class db_table all_db_table_perms; ++ class db_procedure all_db_procedure_perms; ++ class db_column all_db_column_perms; ++ class db_tuple all_db_tuple_perms; ++ class db_blob all_db_blob_perms; ++ ++ attribute sepgsql_client_type; ++ attribute sepgsql_unconfined_type; ++ ++ attribute sepgsql_database_type; ++ attribute sepgsql_table_type; ++ attribute sepgsql_sysobj_table_type; ++ attribute sepgsql_procedure_type; ++ attribute sepgsql_blob_type; ++ attribute sepgsql_module_type; ++ ++ # for regression test ++ type bin_t; ++ type user_home_t; ++ type sepgsql_trusted_proc_exec_t; ++ ++ attribute tmpfile; ++') ++ ++################################# ++# ++# Domain for Testcases ++# ++ ++role sepgsql_test_r; ++ ++userdom_unpriv_user_template(sepgsql_test) ++postgresql_role(sepgsql_test_r, sepgsql_test_t) ++ ++allow sepgsql_test_t tmpfile : dir search_dir_perms; ++allow sepgsql_test_t tmpfile : file rw_file_perms; ++ ++optional_policy(` ++ term_write_all_terms(sepgsql_test_t) ++') ++ ++optional_policy(` ++ gen_require(` ++ type unconfined_t; ++ role unconfined_r; ++ ') ++ ++ tunable_policy(`sepgsql_regression_test_mode',` ++ allow unconfined_t sepgsql_test_t : process transition; ++ ') ++ unconfined_rw_pipes(sepgsql_test_t) ++ role unconfined_r types sepgsql_test_t; ++ role unconfined_r types sepgsql_trusted_proc_t; ++') ++ ++################################# ++# ++# SE-PostgreSQL Declarations ++# ++ ++## ++##

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

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

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

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

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

++##
++gen_tunable(sepgsql_regression_test_mode, false) ++ ++######################################## ++# ++# SE-PostgreSQL audit switch for debugging ++# ++tunable_policy(`sepgsql_enable_auditallow',` ++ auditallow domain sepgsql_database_type : db_database *; ++ auditallow domain sepgsql_table_type : db_table *; ++ auditallow domain sepgsql_table_type : db_column *; ++ auditallow domain sepgsql_table_type : db_tuple { relabelfrom relabelto }; ++ auditallow domain sepgsql_sysobj_table_type : db_tuple *; ++ auditallow domain sepgsql_procedure_type : db_procedure *; ++ auditallow domain sepgsql_blob_type : db_blob *; ++ auditallow domain sepgsql_module_type : db_database { install_module }; ++ auditallow sepgsql_database_type sepgsql_module_type : db_database { load_module }; ++') ++ ++tunable_policy(`! sepgsql_enable_auditdeny',` ++ dontaudit domain sepgsql_database_type : db_database *; ++ dontaudit domain sepgsql_table_type : db_table *; ++ dontaudit domain sepgsql_table_type : db_column *; ++ dontaudit domain sepgsql_table_type : db_tuple { relabelfrom relabelto }; ++ dontaudit domain sepgsql_sysobj_table_type : db_tuple *; ++ dontaudit domain sepgsql_procedure_type : db_procedure *; ++ dontaudit domain sepgsql_blob_type : db_blob *; ++ dontaudit domain sepgsql_module_type : db_database { install_module }; ++ dontaudit sepgsql_database_type sepgsql_module_type : db_database { load_module }; ++') ++ ++######################################## ++# ++# SE-PostgreSQL regression test mode switch ++# ++tunable_policy(`sepgsql_regression_test_mode',` ++ allow sepgsql_client_type user_home_t : db_database { install_module }; ++ allow sepgsql_unconfined_type user_home_t : db_database { install_module }; ++ allow sepgsql_database_type user_home_t : db_database { load_module }; ++') +diff --git a/src/backend/security/sepgsql/selinux.c b/src/backend/security/sepgsql/selinux.c +new file mode 100644 +index 0000000..16f50e0 +--- /dev/null ++++ b/src/backend/security/sepgsql/selinux.c +@@ -0,0 +1,1305 @@ ++/* ++ * src/backend/security/sepgsql/selinux.c ++ * Routines to communicate with SELinux. ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++ ++#include "access/hash.h" ++#include "access/xact.h" ++#include "catalog/pg_security.h" ++#include "lib/stringinfo.h" ++#include "libpq/libpq-be.h" ++#include "libpq/pqsignal.h" ++#include "miscadmin.h" ++#include "security/sepgsql.h" ++#include "storage/fd.h" ++#include "utils/builtins.h" ++#include "utils/memutils.h" ++ ++#include ++#include ++#include ++ ++/* ++ * selinux_catalog ++ * ++ * This static translation lookup table enables to associate a certain ++ * object class/permission name with its internal code, such as ++ * SEPG_CLASS_DB_SCHEMA. ++ * ++ * SELinux requires applications to represent object class and a set of ++ * permissions in code, instead of its name, when we ask SELinux's decision. ++ * ++ * See the definition of security_compute_av(3) API in libselinux. ++ * We need to gives a code of object class, and interpret what permissions ++ * are allowed on the object class from av_decision structure. ++ * Actual values of the code depend on the security policy. In other words, ++ * we cannot know what number is assigned on a certain object class and ++ * permissions. ++ * The string_to_security_class(3) and string_to_av_perm(3) APIs takes ++ * arguments with the name of object class/permission, and returns the ++ * code for the given object class/permissions. ++ * For example, we can know what code is assigned on the "db_table" class ++ * using these functions as follows: ++ * ++ * uint16 tclass_ex = string_to_security_class("db_table"); ++ * ++ * On the other hand, we use an alternative code internally to simplify ++ * the implementation, such as SEPG_CLASS_* for object class. ++ * The following selinux_catalog is used to translate the 'internal' ++ * code and the 'external' code. ++ * ++ * It allows to lookup name of the object class or permission corresponding ++ * to a certain 'internal' code. Then, we can give the name to SELinux's ++ * API to obtain 'external' code which can be used to ask in-kernel SELinux. ++ */ ++static struct ++{ ++ const char *class_name; ++ uint16 class_code; ++ struct ++ { ++ const char *perm_name; ++ uint32 perm_code; ++ } perms[32]; ++} selinux_catalog[] = { ++ { ++ "process", SEPG_CLASS_PROCESS, ++ { ++ {"translation", SEPG_PROCESS__TRANSITION }, ++ {NULL, 0} ++ } ++ }, ++ { ++ "file", SEPG_CLASS_FILE, ++ { ++ {"read", SEPG_FILE__READ }, ++ {"write", SEPG_FILE__WRITE }, ++ {"create", SEPG_FILE__CREATE }, ++ {"getattr", SEPG_FILE__GETATTR }, ++ {NULL, 0} ++ } ++ }, ++ { ++ "dir", SEPG_CLASS_DIR, ++ { ++ {"read", SEPG_DIR__READ }, ++ {"write", SEPG_DIR__WRITE }, ++ {"create", SEPG_DIR__CREATE }, ++ {"getattr", SEPG_DIR__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "lnk_file", SEPG_CLASS_LNK_FILE, ++ { ++ {"read", SEPG_LNK_FILE__READ }, ++ {"write", SEPG_LNK_FILE__WRITE }, ++ {"create", SEPG_LNK_FILE__CREATE }, ++ {"getattr", SEPG_LNK_FILE__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "chr_file", SEPG_CLASS_CHR_FILE, ++ { ++ {"read", SEPG_CHR_FILE__READ }, ++ {"write", SEPG_CHR_FILE__WRITE }, ++ {"create", SEPG_CHR_FILE__CREATE }, ++ {"getattr", SEPG_CHR_FILE__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "blk_file", SEPG_CLASS_BLK_FILE, ++ { ++ {"read", SEPG_BLK_FILE__READ }, ++ {"write", SEPG_BLK_FILE__WRITE }, ++ {"create", SEPG_BLK_FILE__CREATE }, ++ {"getattr", SEPG_BLK_FILE__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "sock_file", SEPG_CLASS_SOCK_FILE, ++ { ++ {"read", SEPG_SOCK_FILE__READ }, ++ {"write", SEPG_SOCK_FILE__WRITE }, ++ {"create", SEPG_SOCK_FILE__CREATE }, ++ {"getattr", SEPG_SOCK_FILE__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "fifo_file", SEPG_CLASS_FIFO_FILE, ++ { ++ {"read", SEPG_FIFO_FILE__READ }, ++ {"write", SEPG_FIFO_FILE__WRITE }, ++ {"create", SEPG_FIFO_FILE__CREATE }, ++ {"getattr", SEPG_FIFO_FILE__GETATTR }, ++ {NULL, 0UL } ++ } ++ }, ++ { ++ "db_database", SEPG_CLASS_DB_DATABASE, ++ { ++ { "create", SEPG_DB_DATABASE__CREATE }, ++ { "drop", SEPG_DB_DATABASE__DROP }, ++ { "getattr", SEPG_DB_DATABASE__GETATTR }, ++ { "setattr", SEPG_DB_DATABASE__SETATTR }, ++ { "relabelfrom", SEPG_DB_DATABASE__RELABELFROM }, ++ { "relabelto", SEPG_DB_DATABASE__RELABELTO }, ++ { "access", SEPG_DB_DATABASE__ACCESS }, ++ { "load_module", SEPG_DB_DATABASE__LOAD_MODULE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_schema", SEPG_CLASS_DB_SCHEMA, ++ { ++ { "create", SEPG_DB_SCHEMA__CREATE }, ++ { "drop", SEPG_DB_SCHEMA__DROP }, ++ { "getattr", SEPG_DB_SCHEMA__GETATTR }, ++ { "setattr", SEPG_DB_SCHEMA__SETATTR }, ++ { "relabelfrom", SEPG_DB_SCHEMA__RELABELFROM }, ++ { "relabelto", SEPG_DB_SCHEMA__RELABELTO }, ++ { "search", SEPG_DB_SCHEMA__SEARCH }, ++ { "add_name", SEPG_DB_SCHEMA__ADD_NAME }, ++ { "remove_name", SEPG_DB_SCHEMA__REMOVE_NAME }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_table", SEPG_CLASS_DB_TABLE, ++ { ++ { "create", SEPG_DB_TABLE__CREATE }, ++ { "drop", SEPG_DB_TABLE__DROP }, ++ { "getattr", SEPG_DB_TABLE__GETATTR }, ++ { "setattr", SEPG_DB_TABLE__SETATTR }, ++ { "relabelfrom", SEPG_DB_TABLE__RELABELFROM }, ++ { "relabelto", SEPG_DB_TABLE__RELABELTO }, ++ { "select", SEPG_DB_TABLE__SELECT }, ++ { "update", SEPG_DB_TABLE__UPDATE }, ++ { "insert", SEPG_DB_TABLE__INSERT }, ++ { "delete", SEPG_DB_TABLE__DELETE }, ++ { "lock", SEPG_DB_TABLE__LOCK }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_view", SEPG_CLASS_DB_VIEW, ++ { ++ { "create", SEPG_DB_VIEW__CREATE }, ++ { "drop", SEPG_DB_VIEW__DROP }, ++ { "getattr", SEPG_DB_VIEW__GETATTR }, ++ { "setattr", SEPG_DB_VIEW__SETATTR }, ++ { "relabelfrom", SEPG_DB_VIEW__RELABELFROM }, ++ { "relabelto", SEPG_DB_VIEW__RELABELTO }, ++ { "usage", SEPG_DB_VIEW__USAGE }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "db_sequence", SEPG_CLASS_DB_SEQUENCE, ++ { ++ { "create", SEPG_DB_SEQUENCE__CREATE }, ++ { "drop", SEPG_DB_SEQUENCE__DROP }, ++ { "getattr", SEPG_DB_SEQUENCE__GETATTR }, ++ { "setattr", SEPG_DB_SEQUENCE__SETATTR }, ++ { "relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM }, ++ { "relabelto", SEPG_DB_SEQUENCE__RELABELTO }, ++ { "get_value", SEPG_DB_SEQUENCE__GET_VALUE }, ++ { "next_value", SEPG_DB_SEQUENCE__NEXT_VALUE }, ++ { "set_value", SEPG_DB_SEQUENCE__SET_VALUE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_procedure", SEPG_CLASS_DB_PROCEDURE, ++ { ++ { "create", SEPG_DB_PROCEDURE__CREATE }, ++ { "drop", SEPG_DB_PROCEDURE__DROP }, ++ { "getattr", SEPG_DB_PROCEDURE__GETATTR }, ++ { "setattr", SEPG_DB_PROCEDURE__SETATTR }, ++ { "relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM }, ++ { "relabelto", SEPG_DB_PROCEDURE__RELABELTO }, ++ { "execute", SEPG_DB_PROCEDURE__EXECUTE }, ++ { "entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT }, ++ { "install", SEPG_DB_PROCEDURE__INSTALL }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_column", SEPG_CLASS_DB_COLUMN, ++ { ++ { "create", SEPG_DB_COLUMN__CREATE }, ++ { "drop", SEPG_DB_COLUMN__DROP }, ++ { "getattr", SEPG_DB_COLUMN__GETATTR }, ++ { "setattr", SEPG_DB_COLUMN__SETATTR }, ++ { "relabelfrom", SEPG_DB_COLUMN__RELABELFROM }, ++ { "relabelto", SEPG_DB_COLUMN__RELABELTO }, ++ { "select", SEPG_DB_COLUMN__SELECT }, ++ { "update", SEPG_DB_COLUMN__UPDATE }, ++ { "insert", SEPG_DB_COLUMN__INSERT }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_tuple", SEPG_CLASS_DB_TUPLE, ++ { ++ { "relabelfrom", SEPG_DB_TUPLE__RELABELFROM }, ++ { "relabelto", SEPG_DB_TUPLE__RELABELTO }, ++ { "select", SEPG_DB_TUPLE__SELECT }, ++ { "update", SEPG_DB_TUPLE__UPDATE }, ++ { "insert", SEPG_DB_TUPLE__INSERT }, ++ { "delete", SEPG_DB_TUPLE__DELETE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_blob", SEPG_CLASS_DB_BLOB, ++ { ++ { "create", SEPG_DB_BLOB__CREATE }, ++ { "drop", SEPG_DB_BLOB__DROP }, ++ { "getattr", SEPG_DB_BLOB__GETATTR }, ++ { "setattr", SEPG_DB_BLOB__SETATTR }, ++ { "relabelfrom", SEPG_DB_BLOB__RELABELFROM }, ++ { "relabelto", SEPG_DB_BLOB__RELABELTO }, ++ { "read", SEPG_DB_BLOB__READ }, ++ { "write", SEPG_DB_BLOB__WRITE }, ++ { "import", SEPG_DB_BLOB__IMPORT }, ++ { "export", SEPG_DB_BLOB__EXPORT }, ++ { NULL, 0UL }, ++ } ++ } ++}; ++ ++/* ++ * GUC option: sepostgresql = [default|enforcing|permissive|disabled] ++ * ++ * SEPGSQL_MODE_DEFAULT : It follows system setting ++ * SEPGSQL_MODE_ENFORCING : Use enforcing mode always ++ * SEPGSQL_MODE_PERMISSIVE : Use permissive mode always ++ * SEPGSQL_MODE_INTERNAL : Internally used mode. Same as permissive mode ++ * except for silence in audit logs ++ * SEPGSQL_MODE_DISABLED : It always disables SE-PgSQL configuration ++ */ ++int sepostgresql_mode; ++ ++/* ++ * userspace access vector cache ++ * ++ * It enables to cache access control decisions in userspace, and minimize ++ * the number of system call invocations. ++ */ ++static MemoryContext AvcMemCtx = NULL; ++ ++#define AVC_HASH_NUM_SLOTS 256 ++#define AVC_HASH_NUM_NODES 180 ++ ++typedef struct _avc_datum ++{ ++ uint32 hash_key; ++ ++ uint16 tclass; ++ sepgsql_sid_t tsid; ++ sepgsql_sid_t nsid; ++ char *tcontext; ++ char *ncontext; ++ ++ uint32 allowed; ++ uint32 auditallow; ++ uint32 auditdeny; ++ bool permissive; ++ ++ bool hot_cache; ++} avc_datum; ++ ++typedef struct _avc_page ++{ ++ struct _avc_page *next; ++ ++ List *slot[AVC_HASH_NUM_SLOTS]; ++ ++ uint32 avc_count; ++ uint32 lru_hint; ++ ++ char scontext[1]; ++} avc_page; ++ ++static avc_page *current_page = NULL; ++ ++static int avc_version; ++ ++/* ++ * selinux_state ++ * ++ * It is deployed on the shared memory region, to show the system ++ * state of SELinux and its security policy. ++ * ++ * The selinux_state->version should be checked prior to avc accesses. ++ * If it does not match with the local avc_version, it means that ++ * system security policy was reloaded or system state (enforcing ++ * or permissive) was changed. ++ * ++ * The state monitoring worker process receives messages from the ++ * kernel using libselinux, and it updates the selinux_state. ++ */ ++struct ++{ ++ int version; ++ ++ bool enforcing; ++} *selinux_state = NULL; ++ ++/* ++ * sepgsqlShmemSize ++ * ++ * It returns required size for shared memory segment ++ */ ++Size ++sepgsqlShmemSize(void) ++{ ++ if (!sepgsqlIsEnabled()) ++ return 0; ++ ++ return sizeof(*selinux_state); ++} ++ ++/* ++ * sepgsqlShmemInit ++ * ++ * It attaches shared memory segment. ++ */ ++static void ++sepgsqlShmemInit(void) ++{ ++ bool found; ++ ++ selinux_state = ShmemInitStruct("SELinux system state", ++ sepgsqlShmemSize(), &found); ++ if (!found) ++ { ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ ++ selinux_state->version = 0; ++ selinux_state->enforcing = (security_getenforce() > 0); ++ ++ LWLockRelease(SepgsqlAvcLock); ++ } ++} ++ ++/* ++ * sepgsqlIsEnabled ++ * sepgsqlIsEnabledBootstrap ++ * ++ * If it returns true, SE-PgSQL is enabled. Otherwise, it is disabled. ++ */ ++bool ++sepgsqlIsEnabledBootstrap(void) ++{ ++ static int enabled = -1; ++ ++ /* ++ * If sepostgresql = off, it is always disabled. ++ */ ++ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED) ++ return false; ++ ++ /* ++ * SE-PgSQL needs SELinux is enabled on the operating system. ++ * If it is disabled, SE-PgSQL has to be also disabled, even if ++ * 'enforcing' or 'permissive' are specified. ++ */ ++ if (enabled < 0) ++ enabled = is_selinux_enabled(); ++ ++ return enabled > 0 ? true : false; ++} ++ ++bool ++sepgsqlIsEnabled(void) ++{ ++ /* ++ * SE-PgSQL is not ready in bootstraping mode, ++ * except for initial labeling process ++ */ ++ if (IsBootstrapProcessingMode()) ++ return false; ++ ++ return sepgsqlIsEnabledBootstrap(); ++} ++ ++/* ++ * sepgsqlGetEnforce ++ * ++ * It returns true, if SE-PgSQL performs in enforcing mode. ++ * ++ * In enforcing mode, SE-PgSQL performs as expected. It checks permissions ++ * on the required action, and it prevents them if violated. ++ * In permissive mode, SE-PgSQL also checks permissions, but it does not ++ * prevent anything, even if violated. It generates audit logs for access ++ * violations, so we can use this mode to debug security policy itself. ++ */ ++bool ++sepgsqlGetEnforce(void) ++{ ++ if (sepostgresql_mode == SEPGSQL_MODE_DEFAULT) ++ { ++ bool rc; ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); ++ rc = selinux_state->enforcing; ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return rc; ++ } ++ else if (sepostgresql_mode == SEPGSQL_MODE_ENFORCING) ++ return true; ++ ++ return false; ++} ++ ++/* ++ * sepgsqlShowMode ++ * ++ * It returns the current performing mode ('selinux_support') ++ * in human readable form. ++ */ ++char * ++sepgsqlShowMode(void) ++{ ++ if (!sepgsqlIsEnabled()) ++ return "disabled"; ++ ++ if (!sepgsqlGetEnforce()) ++ return "permissive"; ++ ++ return "enforcing"; ++} ++ ++/* ++ * sepgsqlGetClientLabel ++ * sepgsqlSetClientLabel ++ * sepgsqlGetServerLabel ++ */ ++static char *clientLabel = NULL; ++ ++char * ++sepgsqlGetClientLabel(void) ++{ ++ if (clientLabel) ++ return clientLabel; ++ ++ if (!MyProcPort) ++ { ++ /* ++ * When this server process was launched in single-user mode, ++ * it does not have any client socket, and the server process also ++ * performs as a client in same time. So, we apply a security context ++ * of the current process as a client's one. ++ * The getcon_raw(3) is an libselinux API to obtain security context ++ * of the current process in raw format. ++ */ ++ if (getprevcon_raw(&clientLabel) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("could not get server's security context"))); ++ } ++ else ++ { ++ /* ++ * Otherwise, SE-PgSQL obtains the security context of the client ++ * process using getpeercon(3). It is an API of SELinux to obtain ++ * the security context of the peer process for the given file ++ * descriptor of the client socket. ++ * For example, a process labeled as "system_u:system_r:httpd_t:s0" ++ * (which is typically apache/httpd) connect to the PgSQL server, ++ * getpeercon_raw() in server side returns the security context ++ * in client side. ++ * If MyProcPort->sock came from unix domain socket, we don't need ++ * any special configuration. OS handles them correctly. ++ * If it is tcp/ip socket, either labeled ipsec or static fallback ++ * context should be configured. ++ * The labeled ipsec is a feature to deliver the security context ++ * of remote peer processes with an enhancement of key exchange ++ * server (racoon). If SELinux is also available in the client host ++ * also, it is the most preferable option. ++ * The static fallback context is a feature to assign an alternative ++ * security context based on the source address and network device ++ * in usage. It can be applied, even if Windows is run on the client. ++ */ ++ if (getpeercon_raw(MyProcPort->sock, &clientLabel) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("could not get client's security context"))); ++ } ++ return clientLabel; ++} ++ ++char * ++sepgsqlSetClientLabel(char *new_label) ++{ ++ char *old_label = clientLabel; ++ avc_page *new_page; ++ int i, length; ++ ++ /* ++ * (1) Set new security context ++ */ ++ clientLabel = new_label; ++ ++ /* ++ * (2) Switch current AVC page ++ */ ++ if (current_page) ++ { ++ new_page = current_page; ++ do { ++ if (strcmp(new_page->scontext, new_label) == 0) ++ { ++ current_page = new_page; ++ return old_label; ++ } ++ new_page = new_page->next; ++ } while (new_page != current_page); ++ } ++ ++ /* Not found, create a new avc_page */ ++ length = sizeof(avc_page) + strlen(new_label); ++ new_page = MemoryContextAllocZero(AvcMemCtx, length); ++ ++ strcpy(new_page->scontext, new_label); ++ for (i=0; i < AVC_HASH_NUM_SLOTS; i++) ++ new_page->slot[i] = NIL; ++ ++ if (!current_page) ++ new_page->next = new_page; ++ else ++ { ++ new_page->next = current_page->next; ++ current_page->next = new_page; ++ } ++ ++ current_page = new_page; ++ ++ /* return old label */ ++ return old_label; ++} ++ ++char * ++sepgsqlGetServerLabel(void) ++{ ++ static char *serverLabel = NULL; ++ ++ if (!serverLabel) ++ { ++ if (getcon_raw(&serverLabel) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("could not get server's security context"))); ++ } ++ return serverLabel; ++} ++ ++/* ++ * sepgsqlAuditLog ++ * ++ * It generates a security audit record. In the default, it writes out ++ * audit records into standard PG's logfile. It also allows to set up ++ * external audit log receiver, such as auditd in Linux, using the ++ * sepgsql_audit_hook. ++ * ++ * SELinux can control what should be audited and should not using ++ * "auditdeny" and "auditallow" rules in the security policy. In the ++ * default, all the access violations are audited, and all the access ++ * allowed are not audited. But we can set up the security policy, so ++ * we can have exceptions. So, it is necessary to follow the suggestion ++ * come from the security policy. (av_decision.auditallow and auditdeny) ++ * ++ * Security audit is an important feature, because it enables us to check ++ * what was happen if we have a security incident. In fact, ISO/IEC15408 ++ * defines several security functionalities for audit features. ++ */ ++static void ++sepgsqlAuditLog(bool denied, char *scontext, char *tcontext, ++ uint16 tclass, uint32 audited, const char *audit_name) ++{ ++ //static int auditfd = -2; ++ StringInfoData buf; ++ const char *tclass_name; ++ const char *perm_name; ++ int i; ++ ++ /* ++ * translation of security contexts to human readable format, ++ * if sepgsql_mcstrans is turned on. ++ */ ++ scontext = sepgsqlTransSecLabelOut(scontext); ++ tcontext = sepgsqlTransSecLabelOut(tcontext); ++ ++ /* lookup name of the object class */ ++ tclass_name = selinux_catalog[tclass].class_name; ++ ++ /* lookup name of the permissions */ ++ initStringInfo(&buf); ++ appendStringInfo(&buf, "{"); ++ ++ for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++) ++ { ++ if (audited & (1UL << i)) ++ { ++ perm_name = selinux_catalog[tclass].perms[i].perm_name; ++ appendStringInfo(&buf, " %s", perm_name); ++ } ++ } ++ appendStringInfo(&buf, " }"); ++ ++ appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, tclass_name); ++ if (audit_name) ++ appendStringInfo(&buf, " name=%s", audit_name); ++ ++ ereport(LOG, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("SELinux: %s %s", ++ (denied ? "denied" : "allowed"), buf.data))); ++} ++ ++/* ++ * computePermsInternal ++ * ++ * It actually asks SELinux what permissions are allowed on a pair of ++ * the security contexts and object class. It also returns what permissions ++ * should be audited on access violation or allowed. ++ * In most cases, subject's security context (scontext) is a client, and ++ * target security context (tcontext) is a database object. ++ * ++ * The access control decision shall be set on the given av_decision. ++ * The av_decision.allowed has a bitmask of SEPG___ ++ * to suggest a set of allowed actions in this object class. ++ */ ++static void ++computePermsInternal(char *scontext, char *tcontext, ++ uint16 tclass, struct av_decision *avd) ++{ ++ const char *tclass_name; ++ security_class_t tclass_ex; ++ struct av_decision avd_ex; ++ int i, deny_unknown = security_deny_unknown(); ++ ++ /* Get external code of the object class*/ ++ Assert(tclass < SEPG_CLASS_MAX); ++ Assert(tclass == selinux_catalog[tclass].class_code); ++ ++ tclass_name = selinux_catalog[tclass].class_name; ++ tclass_ex = string_to_security_class(tclass_name); ++ ++ if (tclass_ex == 0) ++ { ++ /* ++ * If the current security policy does not support permissions ++ * corresponding to database objects, we fill up them with dummy ++ * data. ++ * If security_deny_unknown() returns positive value, undefined ++ * permissions should be denied. Otherwise, allowed ++ */ ++ avd->allowed = (deny_unknown > 0 ? 0 : ~0UL); ++ avd->auditallow = 0UL; ++ avd->auditdeny = ~0UL; ++ avd->flags = 0; ++ ++ return; ++ } ++ ++ /* ++ * Ask SELinux what is allowed set of permissions on a pair of the ++ * security contexts and the given object class. ++ */ ++ if (security_compute_av_flags_raw(scontext, tcontext, ++ tclass_ex, 0, &avd_ex) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux could not compute av_decision: " ++ "scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, tclass_name))); ++ ++ /* ++ * SELinux returns its access control decision as a set of permissions ++ * represented in external code which depends on run-time environment. ++ * So, we need to translate it to the internal representation before ++ * returning results for the caller. ++ */ ++ memset(avd, 0, sizeof(struct av_decision)); ++ ++ for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++) ++ { ++ access_vector_t perm_code_ex; ++ const char *perm_name = selinux_catalog[tclass].perms[i].perm_name; ++ uint32 perm_code = selinux_catalog[tclass].perms[i].perm_code; ++ ++ perm_code_ex = string_to_av_perm(tclass_ex, perm_name); ++ if (perm_code_ex == 0) ++ { ++ /* fill up undefined permissions */ ++ if (!deny_unknown) ++ avd->allowed |= perm_code; ++ avd->auditdeny |= perm_code; ++ ++ continue; ++ } ++ ++ if (avd_ex.allowed & perm_code_ex) ++ avd->allowed |= perm_code; ++ if (avd_ex.auditallow & perm_code_ex) ++ avd->auditallow |= perm_code; ++ if (avd_ex.auditdeny & perm_code_ex) ++ avd->auditdeny |= perm_code; ++ } ++ ++ return; ++} ++ ++/* ++ * sepgsqlComputePerms ++ * ++ * It makes access control decision communicating with SELinux. ++ * If SELinux does not allow required permissions on a pair of the security ++ * contexts, it raises an error or returns false. ++ * ++ * scontext : The security context of subject. In most cases, it is client. ++ * tcontext : The security context of target database object. ++ * tclass : One of the object class code (SEPG_CLASS_*) declared in the ++ * header file. ++ * required : A bitmap of the required permissions (SEPG___) ++ * declared in the header file. ++ * audit_name : A human readable name of the database object for auditing. ++ * abort : True, if caller want to raise an error on access violation. ++ */ ++extern bool ++sepgsqlComputePerms(char *scontext, char *tcontext, ++ uint16 tclass, uint32 required, ++ const char *audit_name, bool abort) ++{ ++ struct av_decision avd; ++ uint32 denied; ++ uint32 audited; ++ ++ computePermsInternal(scontext, tcontext, tclass, &avd); ++ ++ /* ++ * It logs a security audit record for the given request, if necessary. ++ * When SE-PgSQL performs 'internal' mode, it needs to keep silent. ++ */ ++ denied = required & ~avd.allowed; ++ audited = denied ? (denied & avd.auditdeny) ++ : (required & avd.auditallow); ++ ++ if (audited && sepostgresql_mode != SEPGSQL_MODE_INTERNAL) ++ { ++ sepgsqlAuditLog(!!denied, scontext, tcontext, ++ tclass, audited, audit_name); ++ } ++ ++ /* ++ * If here is no policy violations, or SE-PgSQL performs in permissive ++ * mode, or the client process peforms in permissive domain, it returns ++ * normally with 'true'. ++ */ ++ if (!denied || ++ !sepgsqlGetEnforce() || ++ (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) != 0) ++ return true; ++ ++ /* ++ * Otherwise, it raises an error or returns 'false', depending on the ++ * caller's indication by 'abort'. ++ */ ++ if (abort) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("SELinux: security policy violation"))); ++ ++ return false; ++} ++ ++/* ++ * sepgsqlComputeCreate ++ * ++ * It returns a default security context to be assigned on a new database ++ * object. SELinux compute it based on a combination of client, upper object ++ * which owns the new object and object class. ++ * ++ * For example, when a client (staff_u:staff_r:staff_t:s0) tries to create ++ * a new table within a schema (system_u:object_r:sepgsql_schema_t:s0), ++ * SELinux looks-up its security policy. If it has a special rule on the ++ * combination of these security contexts and object class (db_table), ++ * it returns the security context suggested by the special rule. ++ * Otherwise, it returns the security context of schema, as is. ++ * ++ * We expect the caller already applies sanity/validation checks on the ++ * given security context. ++ * ++ * scontext : The security context of subject. In most cases, it is client. ++ * tcontext : The security context of the parent database object.. ++ * tclass : One of the object class code (SEPG_CLASS_*) declared in the ++ * header file. ++ */ ++char * ++sepgsqlComputeCreate(char *scontext, char *tcontext, uint16 tclass) ++{ ++ security_context_t ncontext; ++ security_class_t tclass_ex; ++ const char *tclass_name; ++ char *result; ++ ++ /* Get external code of the object class*/ ++ Assert(tclass < SEPG_CLASS_MAX); ++ Assert(tclass == selinux_catalog[tclass].class_code); ++ ++ tclass_name = selinux_catalog[tclass].class_name; ++ tclass_ex = string_to_security_class(tclass_name); ++ ++ /* ++ * Ask SELinux what is the default context for the given object class ++ * on a pair of security contexts ++ */ ++ if (security_compute_create_raw(scontext, tcontext, ++ tclass_ex, &ncontext)) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux could not compute a new context: " ++ "scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, tclass_name))); ++ /* ++ * libselinux returns malloc()'ed string, so we need to copy it ++ * on the palloc()'ed region. ++ */ ++ PG_TRY(); ++ { ++ result = pstrdup(ncontext); ++ } ++ PG_CATCH(); ++ { ++ freecon(ncontext); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(ncontext); ++ ++ return result; ++} ++ ++/* ++ * sepgsqlAvcReset ++ * ++ * Invalidate all the cached access control decision ++ */ ++static void ++sepgsqlAvcReset(void) ++{ ++ Assert(AvcMemCtx != NULL); ++ ++ MemoryContextReset(AvcMemCtx); ++ ++ current_page = NULL; ++ ++ sepgsqlSetClientLabel(sepgsqlGetClientLabel()); ++} ++ ++static void ++sepgsqlAvcResetOnAbort(XactEvent event, void *arg) ++{ ++ if (event == XACT_EVENT_ABORT) ++ sepgsqlAvcReset(); ++} ++ ++static void ++sepgsqlAvcResetOnSubAbort(SubXactEvent event, SubTransactionId mySubid, ++ SubTransactionId parentSubid, void *arg) ++{ ++ if (event == SUBXACT_EVENT_ABORT_SUB) ++ sepgsqlAvcReset(); ++} ++ ++/* ++ * sepgsqlAvcCheckValid ++ * ++ * It checks whether the current AVC pages are valid, or not. ++ */ ++static bool ++sepgsqlAvcCheckValid(void) ++{ ++ bool result = true; ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); ++ if (avc_version != selinux_state->version) ++ { ++ sepgsqlAvcReset(); ++ ++ /* Copy the current version to local */ ++ avc_version = selinux_state->version; ++ ++ result = false; ++ } ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return result; ++} ++ ++/* ++ * sepgsqlAvcReclaim ++ * ++ * It wipes recently unused AVC entries, if necessary. ++ */ ++static void ++sepgsqlAvcReclaim(avc_page *page) ++{ ++ ListCell *l; ++ avc_datum *cache; ++ ++ while (page->avc_count > AVC_HASH_NUM_NODES - 10) ++ { ++ foreach (l, page->slot[page->lru_hint]) ++ { ++ cache = lfirst(l); ++ ++ if (cache->hot_cache) ++ cache->hot_cache = false; ++ else ++ { ++ list_delete_ptr(page->slot[page->lru_hint], cache); ++ pfree(cache); ++ page->avc_count--; ++ } ++ } ++ page->lru_hint = (page->lru_hint + 1) % AVC_HASH_NUM_SLOTS; ++ } ++} ++ ++/* ++ * sepgsqlAvcMakeEntry ++ * ++ * It makes a new avc entry, and insert it to the given page. ++ */ ++#define avc_hash_key(trelid, tsecid, tclass, nrelid) \ ++ (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3) ^ (nrelid))) ++ ++static avc_datum * ++sepgsqlAvcMakeEntry(avc_page *page, sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) ++{ ++ MemoryContext oldctx; ++ char *scontext; ++ char *tcontext; ++ char *ncontext; ++ avc_datum *cache; ++ uint32 hash_key, index; ++ ++ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid); ++ index = hash_key % AVC_HASH_NUM_SLOTS; ++ ++ oldctx = MemoryContextSwitchTo(AvcMemCtx); ++ ++ scontext = page->scontext; ++ tcontext = securityRawSecLabelOut(tsid.relid, tsid.secid); ++ ncontext = sepgsqlComputeCreate(scontext, tcontext, tclass); ++ ++ cache = palloc0(sizeof(avc_datum)); ++ ++ cache->hash_key = hash_key; ++ ++ cache->tclass = tclass; ++ ++ cache->hot_cache = true; ++ cache->tcontext = tcontext; ++ cache->ncontext = ncontext; ++ cache->tsid.relid = tsid.relid; ++ cache->tsid.secid = tsid.secid; ++ cache->nsid.relid = nrelid; ++ ++ if (OidIsValid(nrelid)) ++ cache->nsid.secid = securityRawSecLabelIn(nrelid, ncontext); ++ else ++ cache->nsid.secid = InvalidOid; ++ ++ if (!OidIsValid(nrelid)) ++ { ++ struct av_decision avd; ++ ++ computePermsInternal(scontext, tcontext, tclass, &avd); ++ cache->allowed = avd.allowed; ++ cache->auditallow = avd.auditallow; ++ cache->auditdeny = avd.auditdeny; ++ ++ if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) ++ cache->permissive = true; ++ } ++ ++ if (page->avc_count > AVC_HASH_NUM_NODES) ++ sepgsqlAvcReclaim(page); ++ ++ page->slot[index] = lcons(cache, page->slot[index]); ++ page->avc_count++; ++ ++ MemoryContextSwitchTo(oldctx); ++ ++ return cache; ++} ++ ++/* ++ * sepgsqlAvcLookup ++ * ++ * It lookups required AVC entry ++ */ ++static avc_datum * ++sepgsqlAvcLookup(avc_page *page, sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) ++{ ++ avc_datum *cache = NULL; ++ uint32 hash_key, index; ++ ListCell *l; ++ ++ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid); ++ index = hash_key % AVC_HASH_NUM_SLOTS; ++ ++ foreach (l, page->slot[index]) ++ { ++ cache = lfirst(l); ++ if (cache->hash_key == hash_key && ++ cache->tclass == tclass && ++ cache->tsid.relid == tsid.relid && ++ cache->tsid.secid == tsid.secid && ++ cache->nsid.relid == nrelid) ++ { ++ cache->hot_cache = true; ++ return cache; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * sepgsqlClientHasPerms ++ * ++ * It checks client's privileges on the given object using avc. ++ */ ++bool ++sepgsqlClientHasPerms(sepgsql_sid_t tsid, ++ uint16 tclass, uint32 required, ++ const char *audit_name, bool abort) ++{ ++ avc_datum *cache; ++ uint32 denied, audited; ++ bool result = true; ++ ++ do { ++ cache = sepgsqlAvcLookup(current_page, tsid, tclass, InvalidOid); ++ if (!cache) ++ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass, InvalidOid); ++ } while (!sepgsqlAvcCheckValid()); ++ ++ denied = required & ~cache->allowed; ++ audited = denied ? (denied & cache->auditdeny) ++ : (required & cache->auditallow); ++ if (audited) ++ { ++ sepgsqlAuditLog(!!denied, ++ current_page->scontext, ++ securityRawSecLabelOut(tsid.relid, tsid.secid), ++ cache->tclass, audited, audit_name); ++ } ++ ++ if (denied) ++ { ++ if (!sepgsqlGetEnforce() || cache->permissive) ++ cache->allowed |= required; /* prevent flood of audit log */ ++ else ++ { ++ if (abort) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("SELinux: security policy violation"))); ++ result = false; ++ } ++ } ++ ++ return result; ++} ++ ++/* ++ * sepgsqlClientCreateSecid ++ * sepgsqlClientCreateLabel ++ */ ++sepgsql_sid_t ++sepgsqlClientCreateSecid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) ++{ ++ avc_datum *cache; ++ ++ do { ++ cache = sepgsqlAvcLookup(current_page, tsid, tclass, nrelid); ++ if (!cache) ++ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass, nrelid); ++ } while (!sepgsqlAvcCheckValid()); ++ ++ return cache->nsid; ++} ++ ++security_context_t ++sepgsqlClientCreateLabel(sepgsql_sid_t tsid, uint16 tclass) ++{ ++ avc_datum *cache; ++ ++ do { ++ cache = sepgsqlAvcLookup(current_page, tsid, tclass, InvalidOid); ++ if (!cache) ++ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass, InvalidOid); ++ } while (!sepgsqlAvcCheckValid()); ++ ++ return cache->ncontext; ++} ++ ++/* ++ * SELinux state monitoring process ++ * ++ * This process is forked from postmaster to monitor the state of SELinux. ++ * SELinux can make a notifier message to userspace object manager via ++ * netlink socket. When it receives the message, it updates selinux_state ++ * structure assigned on shared memory region to make any instance reset ++ * its AVC soon. ++ */ ++static int ++sepgsql_cb_log(int type, const char *fmt, ...) ++{ ++ char *c, buffer[1024]; ++ va_list ap; ++ ++ va_start(ap, fmt); ++ vsnprintf(buffer, sizeof(buffer), fmt, ap); ++ va_end(ap); ++ ++ c = strrchr(buffer, '\n'); ++ if (c) ++ *c = '\0'; ++ ++ ereport(LOG,(errmsg("%s", buffer))); ++ ++ return 0; ++} ++ ++static int ++sepgsql_cb_setenforce(int enforce) ++{ ++ /* switch enforcing/permissive */ ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ selinux_state->enforcing = (enforce ? true : false); ++ selinux_state->version++; ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return 0; ++} ++ ++static int ++sepgsql_cb_policyload(int seqno) ++{ ++ /* invalidate local avc */ ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ selinux_state->version++; ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return 0; ++} ++ ++bool ++sepgsqlReceiverStart(void) ++{ ++ return sepgsqlIsEnabled(); ++} ++ ++void ++sepgsqlReceiverMain(void) ++{ ++ union selinux_callback cb; ++ ++ Assert(sepgsqlIsEnabled()); ++ ++#ifdef HAVE_SETSID ++ if (setsid() < 0) ++ elog(FATAL, "setsid() failed: %m"); ++#endif ++ ++ /* ++ * setup the signal handler ++ */ ++ pqinitmask(); ++ pqsignal(SIGHUP, SIG_IGN); ++ pqsignal(SIGINT, SIG_IGN); ++ pqsignal(SIGTERM, exit); ++ pqsignal(SIGQUIT, exit); ++ pqsignal(SIGUSR1, SIG_IGN); ++ pqsignal(SIGUSR2, SIG_IGN); ++ pqsignal(SIGCHLD, SIG_DFL); ++ PG_SETMASK(&UnBlockSig); ++ ++ /* ++ * map shared memory segment ++ */ ++ sepgsqlShmemInit(); ++ ++ ereport(LOG, (errmsg("SELinux: netlink receiver (pid=%u)", getpid()))); ++ ++ /* ++ * setup callback functions from avc_netlink_loop() ++ */ ++ cb.func_log = sepgsql_cb_log; ++ selinux_set_callback(SELINUX_CB_LOG, cb); ++ cb.func_setenforce = sepgsql_cb_setenforce; ++ selinux_set_callback(SELINUX_CB_SETENFORCE, cb); ++ cb.func_policyload = sepgsql_cb_policyload; ++ selinux_set_callback(SELINUX_CB_POLICYLOAD, cb); ++ ++ /* ++ * open netlink socket and wait for messages ++ */ ++ avc_netlink_open(1); ++ ++ avc_netlink_loop(); ++ ++ exit(0); ++} ++ ++/* ++ * sepgsqlInitialize ++ * ++ * It sets up the privilege (security context) of the client and initializes ++ * a few internal stuff. ++ */ ++void ++sepgsqlInitialize(void) ++{ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* ++ * SE-PgSQL does not prevent anything in single-user mode. ++ */ ++ if (!MyProcPort) ++ sepostgresql_mode = SEPGSQL_MODE_INTERNAL; ++ ++ sepgsqlShmemInit(); ++ ++ AvcMemCtx = AllocSetContextCreate(TopMemoryContext, ++ "SE-PgSQL userspace AVC", ++ ALLOCSET_DEFAULT_MINSIZE, ++ ALLOCSET_DEFAULT_INITSIZE, ++ ALLOCSET_DEFAULT_MAXSIZE); ++ ++ RegisterXactCallback(sepgsqlAvcResetOnAbort, NULL); ++ RegisterSubXactCallback(sepgsqlAvcResetOnSubAbort, NULL); ++ ++ /* ++ * Set client's security context ++ */ ++ sepgsqlSetClientLabel(sepgsqlGetClientLabel()); ++} +diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c +index fd248fc..b24239d 100644 +--- a/src/backend/storage/file/fd.c ++++ b/src/backend/storage/file/fd.c +@@ -1329,6 +1329,13 @@ FileTruncate(File file, off_t offset) + return returnCode; + } + ++int ++FileRawDescriptor(File file) ++{ ++ Assert(FileIsValid(file)); ++ ++ return VfdCache[file].fd; ++} + + /* + * Routines that want to use stdio (ie, FILE*) should use AllocateFile +diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c +index 3022867..cf6fc3d 100644 +--- a/src/backend/storage/ipc/ipci.c ++++ b/src/backend/storage/ipc/ipci.c +@@ -25,6 +25,7 @@ + #include "postmaster/autovacuum.h" + #include "postmaster/bgwriter.h" + #include "postmaster/postmaster.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/ipc.h" + #include "storage/pg_shmem.h" +@@ -119,6 +120,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) + #ifdef EXEC_BACKEND + size = add_size(size, ShmemBackendArraySize()); + #endif ++ size = add_size(size, sepgsqlShmemSize()); + + /* freeze the addin request size and include it */ + addin_request_allowed = false; +diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c +index a946972..7abf8e3 100644 +--- a/src/backend/storage/large_object/inv_api.c ++++ b/src/backend/storage/large_object/inv_api.c +@@ -197,14 +197,14 @@ getbytealen(bytea *data) + * in use. + */ + Oid +-inv_create(Oid lobjId) ++inv_create(Oid lobjId, Oid secid) + { + Oid lobjId_new; + + /* + * Create a new largeobject with empty data pages + */ +- lobjId_new = LargeObjectCreate(lobjId); ++ lobjId_new = LargeObjectCreate(lobjId, secid); + + /* + * dependency on the owner of largeobject +diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c +index ab2249a..bcc5448 100644 +--- a/src/backend/tcop/fastpath.c ++++ b/src/backend/tcop/fastpath.c +@@ -26,6 +26,7 @@ + #include "libpq/pqformat.h" + #include "mb/pg_wchar.h" + #include "miscadmin.h" ++#include "security/sepgsql.h" + #include "tcop/fastpath.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" +@@ -343,11 +344,13 @@ HandleFunctionRequest(StringInfo msgBuf) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(fip->namespace)); ++ sepgsql_schema_search(fip->namespace, true); + + aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(fid)); ++ sepgsql_proc_execute(fid); + + /* + * Prepare function call info block and insert arguments. +diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c +index 61b329d..5a379aa 100644 +--- a/src/backend/tcop/pquery.c ++++ b/src/backend/tcop/pquery.c +@@ -573,7 +573,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) + Assert(pstmt->returningLists); + portal->tupDesc = + ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), +- false); ++ false, false); + } + + /* +diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c +index 9e82a48..96d25c6 100644 +--- a/src/backend/tcop/utility.c ++++ b/src/backend/tcop/utility.c +@@ -50,6 +50,7 @@ + #include "postmaster/bgwriter.h" + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteRemove.h" ++#include "security/sepgsql.h" + #include "storage/fd.h" + #include "tcop/pquery.h" + #include "tcop/utility.h" +@@ -162,6 +163,7 @@ check_xact_readonly(Node *parsetree) + case T_AlterRoleSetStmt: + case T_AlterObjectSchemaStmt: + case T_AlterOwnerStmt: ++ case T_AlterSecLabelStmt: + case T_AlterSeqStmt: + case T_AlterTableStmt: + case T_RenameStmt: +@@ -634,6 +636,10 @@ ProcessUtility(Node *parsetree, + ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); + break; + ++ case T_AlterSecLabelStmt: ++ ExecAlterSecLabelStmt((AlterSecLabelStmt *) parsetree); ++ break; ++ + case T_AlterTableStmt: + { + List *stmts; +@@ -917,6 +923,7 @@ ProcessUtility(Node *parsetree, + LoadStmt *stmt = (LoadStmt *) parsetree; + + closeAllVfds(); /* probably not necessary... */ ++ + /* Allowed names are restricted if you're not superuser */ + load_file(stmt->filename, !superuser()); + } +@@ -1664,6 +1671,31 @@ CreateCommandTag(Node *parsetree) + } + break; + ++ case T_AlterSecLabelStmt: ++ switch (((AlterSecLabelStmt *) parsetree)->objectType) ++ { ++ case OBJECT_DATABASE: ++ tag = "ALTER DATABASE"; ++ break; ++ case OBJECT_SCHEMA: ++ tag = "ALTER SCHEMA"; ++ break; ++ case OBJECT_TABLE: ++ case OBJECT_COLUMN: ++ tag = "ALTER TABLE"; ++ break; ++ case OBJECT_SEQUENCE: ++ tag = "ALTER SEQUENCE"; ++ break; ++ case OBJECT_FUNCTION: ++ tag = "ALTER FUNCTION"; ++ break; ++ default: ++ tag = "???"; ++ break; ++ } ++ break; ++ + case T_AlterTableStmt: + switch (((AlterTableStmt *) parsetree)->relkind) + { +@@ -2242,6 +2274,10 @@ GetCommandLogLevel(Node *parsetree) + lev = LOGSTMT_DDL; + break; + ++ case T_AlterSecLabelStmt: ++ lev = LOGSTMT_DDL; ++ break; ++ + case T_AlterTableStmt: + lev = LOGSTMT_DDL; + break; +diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c +index e1328dc..7041047 100644 +--- a/src/backend/utils/adt/genfile.c ++++ b/src/backend/utils/adt/genfile.c +@@ -24,6 +24,7 @@ + #include "funcapi.h" + #include "miscadmin.h" + #include "postmaster/syslogger.h" ++#include "security/sepgsql.h" + #include "storage/fd.h" + #include "utils/builtins.h" + #include "utils/memutils.h" +@@ -99,6 +100,9 @@ pg_read_file(PG_FUNCTION_ARGS) + + filename = convert_and_check_filename(filename_t); + ++ /* SELinux: check file:{read} permission */ ++ sepgsql_file_read(filename); ++ + if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL) + ereport(ERROR, + (errcode_for_file_access(), +@@ -159,6 +163,8 @@ pg_stat_file(PG_FUNCTION_ARGS) + (errmsg("must be superuser to get file information")))); + + filename = convert_and_check_filename(filename_t); ++ /* SELinux: check file:{getattr} permission */ ++ sepgsql_file_stat(filename); + + if (stat(filename, &fst) < 0) + ereport(ERROR, +diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c +index 81684e3..b681214 100644 +--- a/src/backend/utils/adt/ri_triggers.c ++++ b/src/backend/utils/adt/ri_triggers.c +@@ -39,6 +39,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_relation.h" + #include "miscadmin.h" ++#include "security/rowlevel.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -2627,6 +2628,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) + const char *sep; + int i; + int old_work_mem; ++ int save_rowlv; + char workmembuf[32]; + int spi_result; + SPIPlanPtr qplan; +@@ -2759,6 +2761,11 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) + SPI_result, querybuf.data); + + /* ++ * Disables the Row-level stuff during the internal consistency checks. ++ */ ++ save_rowlv = rowlvSetPerformingMode(ROWLV_BYPASS_MODE); ++ ++ /* + * Run the plan. For safety we force a current snapshot to be used. (In + * serializable mode, this arguably violates serializability, but we + * really haven't got much choice.) We don't need to register the +@@ -2771,6 +2778,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) + InvalidSnapshot, + true, false, 1); + ++ /* Restore Row-level stuff */ ++ rowlvSetPerformingMode(save_rowlv); ++ + /* Check result */ + if (spi_result != SPI_OK_SELECT) + elog(ERROR, "SPI_execute_snapshot returned %d", spi_result); +@@ -3265,6 +3275,7 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan, + int spi_result; + Oid save_userid; + int save_sec_context; ++ int save_rowlv, temp_rowlv; + Datum vals[RI_MAX_NUMKEYS * 2]; + char nulls[RI_MAX_NUMKEYS * 2]; + +@@ -3348,12 +3359,19 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan, + SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner, + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); + ++ /* Switch Row-level stuff behavior on FK checks, if necessary */ ++ temp_rowlv = (detectNewRows ? ROWLV_ABORT_MODE : ROWLV_FILTER_MODE); ++ save_rowlv = rowlvSetPerformingMode(temp_rowlv); ++ + /* Finally we can run the query. */ + spi_result = SPI_execute_snapshot(qplan, + vals, nulls, + test_snapshot, crosscheck_snapshot, + false, false, limit); + ++ /* Restore Row-level stuff behavior */ ++ rowlvSetPerformingMode(save_rowlv); ++ + /* Restore UID and security context */ + SetUserIdAndSecContext(save_userid, save_sec_context); + +diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c +index 5bb166d..ef9c2e9 100644 +--- a/src/backend/utils/adt/tid.c ++++ b/src/backend/utils/adt/tid.c +@@ -27,6 +27,7 @@ + #include "libpq/pqformat.h" + #include "miscadmin.h" + #include "parser/parsetree.h" ++#include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/rel.h" +@@ -347,6 +348,8 @@ currtid_byreloid(PG_FUNCTION_ARGS) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_relation_get_transaction_id(RelationGetRelid(rel)); + + if (rel->rd_rel->relkind == RELKIND_VIEW) + return currtid_for_view(rel, tid); +@@ -377,6 +380,8 @@ currtid_byrelname(PG_FUNCTION_ARGS) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_relation_get_transaction_id(RelationGetRelid(rel)); + + if (rel->rd_rel->relkind == RELKIND_VIEW) + return currtid_for_view(rel, tid); +diff --git a/src/backend/utils/adt/trigfuncs.c b/src/backend/utils/adt/trigfuncs.c +index ceab88d..80db58d 100644 +--- a/src/backend/utils/adt/trigfuncs.c ++++ b/src/backend/utils/adt/trigfuncs.c +@@ -76,6 +76,10 @@ suppress_redundant_updates_trigger(PG_FUNCTION_ARGS) + !OidIsValid(HeapTupleHeaderGetOid(newheader))) + HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader)); + ++ if (HeapTupleHeaderHasSecid(newheader) && ++ !OidIsValid(HeapTupleHeaderGetSecid(newheader))) ++ HeapTupleHeaderSetSecid(newheader, HeapTupleHeaderGetSecid(oldheader)); ++ + /* if the tuple payload is the same ... */ + if (newtuple->t_len == oldtuple->t_len && + newheader->t_hoff == oldheader->t_hoff && +diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c +index 8fc2d5a..b943e80 100644 +--- a/src/backend/utils/cache/plancache.c ++++ b/src/backend/utils/cache/plancache.c +@@ -859,12 +859,12 @@ PlanCacheComputeResultDesc(List *stmt_list) + if (IsA(node, Query)) + { + query = (Query *) node; +- return ExecCleanTypeFromTL(query->targetList, false); ++ return ExecCleanTypeFromTL(query->targetList, false, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; +- return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); ++ return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false); + } + /* other cases shouldn't happen, but return NULL */ + break; +@@ -875,13 +875,14 @@ PlanCacheComputeResultDesc(List *stmt_list) + { + query = (Query *) node; + Assert(query->returningList); +- return ExecCleanTypeFromTL(query->returningList, false); ++ return ExecCleanTypeFromTL(query->returningList, false, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; + Assert(pstmt->returningLists); +- return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false); ++ return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), ++ false, false); + } + /* other cases shouldn't happen, but return NULL */ + break; +diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c +index df0a172..a48d474 100644 +--- a/src/backend/utils/cache/relcache.c ++++ b/src/backend/utils/cache/relcache.c +@@ -55,6 +55,7 @@ + #include "optimizer/prep.h" + #include "optimizer/var.h" + #include "rewrite/rewriteDefine.h" ++#include "security/sepgsql.h" + #include "storage/fd.h" + #include "storage/lmgr.h" + #include "storage/smgr.h" +@@ -865,6 +866,10 @@ RelationBuildDesc(Oid targetRelId, Relation oldrelation) + /* extract reloptions if any */ + RelationParseRelOptions(relation, pg_class_tuple); + ++ /* Fixup relation->rd_att->tdhassecid */ ++ RelationGetDescr(relation)->tdhassecid ++ = securityTupleDescHasSecid(relid, relp->relkind); ++ + /* + * initialize the relation lock manager information + */ +@@ -1460,6 +1465,11 @@ formrdesc(const char *relationName, Oid relationReltype, + RelationGetRelid(relation) = relation->rd_att->attrs[0]->attrelid; + relation->rd_rel->relfilenode = RelationGetRelid(relation); + ++ /* Fixup relation->rd_att->tdhassecid */ ++ RelationGetDescr(relation)->tdhassecid ++ = securityTupleDescHasSecid(RelationGetRelid(relation), ++ RELKIND_RELATION); ++ + /* + * initialize the relation lock manager information + */ +@@ -2749,6 +2759,13 @@ BuildHardcodedDescriptor(int natts, Form_pg_attribute attrs, bool hasoids) + result = CreateTemplateTupleDesc(natts, hasoids); + result->tdtypeid = RECORDOID; /* not right, but we don't care */ + result->tdtypmod = -1; ++ /* ++ * NOTE: we assume the returned TupleDesc is only used for ++ * references to toast'ed data, and it is not delivered to ++ * heap_form_tuple(), so TupleDesc->tdhassecid don't give us ++ * any effect. ++ * We omit to invoke securityTupleDescHasSecid() here. ++ */ + + for (i = 0; i < natts; i++) + { +@@ -3503,6 +3520,11 @@ load_relcache_init_file(void) + rel->rd_options = NULL; + } + ++ /* Fixup rel->rd_att->tdhassecid */ ++ RelationGetDescr(rel)->tdhassecid ++ = securityTupleDescHasSecid(RelationGetRelid(rel), ++ RelationGetForm(rel)->relkind); ++ + /* mark not-null status */ + if (has_not_null) + { +diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c +index 529940c..fe06e2f 100644 +--- a/src/backend/utils/fmgr/dfmgr.c ++++ b/src/backend/utils/fmgr/dfmgr.c +@@ -23,6 +23,7 @@ + #endif + #include "lib/stringinfo.h" + #include "miscadmin.h" ++#include "security/sepgsql.h" + #include "utils/dynamic_loader.h" + #include "utils/hsearch.h" + +@@ -109,6 +110,9 @@ load_external_function(char *filename, char *funcname, + /* Expand the possibly-abbreviated filename to an exact path name */ + fullname = expand_dynamic_library_name(filename); + ++ /* SELinux checks db_database:{load_module} */ ++ sepgsql_database_load_module(MyDatabaseId, fullname); ++ + /* Load the shared library, unless we already did */ + lib_handle = internal_load_library(fullname); + +@@ -149,6 +153,9 @@ load_file(const char *filename, bool restricted) + /* Expand the possibly-abbreviated filename to an exact path name */ + fullname = expand_dynamic_library_name(filename); + ++ /* SELinux checks db_database:{load_module} */ ++ sepgsql_database_load_module(MyDatabaseId, fullname); ++ + /* Unload the library if currently loaded */ + internal_unload_library(fullname); + +diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c +index 9a1da59..76264f3 100644 +--- a/src/backend/utils/fmgr/fmgr.c ++++ b/src/backend/utils/fmgr/fmgr.c +@@ -24,6 +24,7 @@ + #include "miscadmin.h" + #include "nodes/nodeFuncs.h" + #include "pgstat.h" ++#include "security/sepgsql.h" + #include "utils/builtins.h" + #include "utils/fmgrtab.h" + #include "utils/guc.h" +@@ -232,6 +233,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, + */ + if (!ignore_security && + (procedureStruct->prosecdef || ++ sepgsql_proc_entrypoint(procedureTuple) || + !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig))) + { + finfo->fn_addr = fmgr_security_definer; +@@ -860,6 +862,7 @@ struct fmgr_security_definer_cache + { + FmgrInfo flinfo; /* lookup info for target function */ + Oid userid; /* userid to set, or InvalidOid */ ++ char *seclabel; /* security label to set, or NULL */ + ArrayType *proconfig; /* GUC values to set, or NULL */ + }; + +@@ -881,6 +884,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS) + FmgrInfo *save_flinfo; + Oid save_userid; + int save_sec_context; ++ char *save_label = NULL; + volatile int save_nestlevel; + PgStat_FunctionCallUsage fcusage; + +@@ -910,6 +914,9 @@ fmgr_security_definer(PG_FUNCTION_ARGS) + if (procedureStruct->prosecdef) + fcache->userid = procedureStruct->proowner; + ++ fcache->seclabel ++ = sepgsql_proc_trusted(tuple, fcinfo->flinfo->fn_mcxt); ++ + datum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proconfig, + &isnull); + if (!isnull) +@@ -936,6 +943,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS) + if (OidIsValid(fcache->userid)) + SetUserIdAndSecContext(fcache->userid, + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); ++ if (fcache->seclabel) ++ save_label = sepgsqlSetClientLabel(fcache->seclabel); + + if (fcache->proconfig) + { +@@ -983,6 +992,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS) + AtEOXact_GUC(true, save_nestlevel); + if (OidIsValid(fcache->userid)) + SetUserIdAndSecContext(save_userid, save_sec_context); ++ if (fcache->seclabel) ++ sepgsqlSetClientLabel(save_label); + + return result; + } +diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c +index 327ba7c..0fc1c8d 100644 +--- a/src/backend/utils/init/postinit.c ++++ b/src/backend/utils/init/postinit.c +@@ -32,6 +32,7 @@ + #include "pgstat.h" + #include "postmaster/autovacuum.h" + #include "postmaster/postmaster.h" ++#include "security/sepgsql.h" + #include "storage/backendid.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" +@@ -201,7 +202,7 @@ CheckMyDatabase(const char *name, bool am_superuser) + name))); + + /* +- * Check privilege to connect to the database. (The am_superuser test ++ * Check privilege to connect to the database. (The am_superuser test + * is redundant, but since we have the flag, might as well check it + * and save a few cycles.) + */ +@@ -213,6 +214,9 @@ CheckMyDatabase(const char *name, bool am_superuser) + errmsg("permission denied for database \"%s\"", name), + errdetail("User does not have CONNECT privilege."))); + ++ /* SELinux: db_database:{access} */ ++ sepgsql_database_access(MyDatabaseId); ++ + /* + * Check connection limit for this database. + * +@@ -607,6 +611,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, + /* set up ACL framework (so CheckMyDatabase can check permissions) */ + initialize_acl(); + ++ /* Initialize SE-PostgreSQL */ ++ sepgsqlInitialize(); ++ + /* + * Read the real pg_database row for our database, check permissions and + * set up database-specific GUC settings. We can't do this until all the +diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c +index db1d933..568ef80 100644 +--- a/src/backend/utils/misc/guc.c ++++ b/src/backend/utils/misc/guc.c +@@ -57,6 +57,7 @@ + #include "postmaster/syslogger.h" + #include "postmaster/walwriter.h" + #include "regex/regex.h" ++#include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "tcop/tcopprot.h" +@@ -257,6 +258,18 @@ static const struct config_enum_entry isolation_level_options[] = { + {NULL, 0} + }; + ++#ifdef HAVE_SELINUX ++static const struct config_enum_entry sepostgresql_mode_options [] = { ++ {"on", SEPGSQL_MODE_DEFAULT, true}, ++ {"off", SEPGSQL_MODE_DISABLED, true}, ++ {"default", SEPGSQL_MODE_DEFAULT, false}, ++ {"permissive", SEPGSQL_MODE_PERMISSIVE, false}, ++ {"enforcing", SEPGSQL_MODE_ENFORCING, false}, ++ {"disabled", SEPGSQL_MODE_DISABLED, false}, ++ {NULL, 0} ++}; ++#endif ++ + static const struct config_enum_entry session_replication_role_options[] = { + {"origin", SESSION_REPLICATION_ROLE_ORIGIN, false}, + {"replica", SESSION_REPLICATION_ROLE_REPLICA, false}, +@@ -1221,6 +1234,24 @@ static struct config_bool ConfigureNamesBool[] = + &IgnoreSystemIndexes, + false, NULL, NULL + }, ++#ifdef HAVE_SELINUX ++ { ++ {"sepostgresql_row_level", PGC_POSTMASTER, CONN_AUTH_SECURITY, ++ gettext_noop("Row-level access controls on SE-PostgreSQL"), ++ NULL, ++ }, ++ &sepostgresql_row_level, ++ true, NULL, NULL ++ }, ++ { ++ {"sepostgresql_mcstrans", PGC_USERSET, CONN_AUTH_SECURITY, ++ gettext_noop("SE-PostgreSQL uses mcstrans on printing security labels"), ++ NULL, ++ }, ++ &sepostgresql_mcstrans, ++ true, NULL, NULL ++ }, ++#endif + + { + {"lo_compat_privileges", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS, +@@ -2640,7 +2671,17 @@ static struct config_enum ConfigureNamesEnum[] = + ®ex_flavor, + REG_ADVANCED, regex_flavor_options, NULL, NULL + }, +- ++#ifdef HAVE_SELINUX ++ { ++ {"sepostgresql", PGC_POSTMASTER, CONN_AUTH_SECURITY, ++ gettext_noop("SE-PostgreSQL performing mode"), ++ NULL, ++ }, ++ &sepostgresql_mode, ++ SEPGSQL_MODE_DISABLED, sepostgresql_mode_options, ++ NULL, sepgsqlShowMode ++ }, ++#endif + { + {"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets the session's behavior for triggers and rewrite rules."), +diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample +index 85acc4e..e298197 100644 +--- a/src/backend/utils/misc/postgresql.conf.sample ++++ b/src/backend/utils/misc/postgresql.conf.sample +@@ -51,7 +51,7 @@ + + + #------------------------------------------------------------------------------ +-# CONNECTIONS AND AUTHENTICATION ++# CONNECTIONS, AUTHENTICATION AND SECURITY + #------------------------------------------------------------------------------ + + # - Connection Settings - +@@ -95,7 +95,7 @@ + # 0 selects the system default + #tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default +- ++#sepostgresql = off # SE-PostgreSQL support + + #------------------------------------------------------------------------------ + # RESOURCE USAGE (except WAL) +diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c +index 1fcf590..620ac5c 100644 +--- a/src/bin/initdb/initdb.c ++++ b/src/bin/initdb/initdb.c +@@ -87,6 +87,7 @@ static bool debug = false; + static bool noclean = false; + static bool show_setting = false; + static char *xlog_dir = ""; ++static bool enable_selinux = false; + + + /* internal vars */ +@@ -1205,6 +1206,13 @@ setup_config(void) + "#default_text_search_config = 'pg_catalog.simple'", + repltok); + ++ if (enable_selinux) ++ { ++ strcpy(repltok, "sepostgresql = on"); ++ conflines = replace_token(conflines, ++ "#sepostgresql = off", repltok); ++ } ++ + snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data); + + writefile(path, conflines); +@@ -2444,6 +2452,7 @@ usage(const char *progname) + printf(_(" -U, --username=NAME database superuser name\n")); + printf(_(" -W, --pwprompt prompt for a password for the new superuser\n")); + printf(_(" -X, --xlogdir=XLOGDIR location for the transaction log directory\n")); ++ printf(_(" --enable-selinux enables SELinux support, if compiled\n")); + printf(_("\nLess commonly used options:\n")); + printf(_(" -d, --debug generate lots of debugging output\n")); + printf(_(" -L DIRECTORY where to find the input files\n")); +@@ -2479,6 +2488,7 @@ main(int argc, char *argv[]) + {"auth", required_argument, NULL, 'A'}, + {"pwprompt", no_argument, NULL, 'W'}, + {"pwfile", required_argument, NULL, 9}, ++ {"enable-selinux", no_argument, NULL, 10}, + {"username", required_argument, NULL, 'U'}, + {"help", no_argument, NULL, '?'}, + {"version", no_argument, NULL, 'V'}, +@@ -2595,6 +2605,9 @@ main(int argc, char *argv[]) + case 9: + pwfilename = xstrdup(optarg); + break; ++ case 10: ++ enable_selinux = true; ++ break; + case 's': + show_setting = true; + break; +diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c +index 57b5f7d..4e48601 100644 +--- a/src/bin/pg_dump/pg_dump.c ++++ b/src/bin/pg_dump/pg_dump.c +@@ -112,6 +112,8 @@ static int disable_dollar_quoting = 0; + static int dump_inserts = 0; + static int column_inserts = 0; + ++/* flag to turn on/off security_context */ ++static int security_context = 0; + + static void help(const char *progname); + static void expand_schema_name_patterns(SimpleStringList *patterns, +@@ -277,6 +279,7 @@ main(int argc, char **argv) + {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, + {"role", required_argument, NULL, 3}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"security-context", no_argument, &security_context, 1}, + + {NULL, 0, NULL, 0} + }; +@@ -425,6 +428,8 @@ main(int argc, char **argv) + outputNoTablespaces = 1; + else if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; ++ else if (strcmp(optarg, "security-context") == 0) ++ security_context = 1; + else + { + fprintf(stderr, +@@ -573,6 +578,28 @@ main(int argc, char **argv) + std_strings = PQparameterStatus(g_conn, "standard_conforming_strings"); + g_fout->std_strings = (std_strings && strcmp(std_strings, "on") == 0); + ++ /* Check availability of SE-PostgreSQL */ ++ if (security_context > 0) ++ { ++ PGresult *res; ++ ++ res = PQexec(g_conn, "SHOW sepostgresql"); ++ if (PQresultStatus(res) != PGRES_TUPLES_OK || ++ PQntuples(res) != 1 || ++ strcmp(PQgetvalue(res, 0, 0), "on") != 0) ++ { ++ write_msg(NULL, "SE-PostgreSQL is not available now."); ++ exit(1); ++ } ++ } ++ ++ /* ++ * It needs to force column insertion mode, when --inserts ++ * and either --security-label or --security-acl is given. ++ */ ++ if (security_context > 0 && dump_inserts) ++ column_inserts = 1; ++ + /* Set the role if requested */ + if (use_role && g_fout->remoteVersion >= 80100) + { +@@ -826,6 +853,8 @@ help(const char *progname) + printf(_(" --use-set-session-authorization\n" + " use SET SESSION AUTHORIZATION commands instead of\n" + " ALTER OWNER commands to set ownership\n")); ++ printf(_(" --security-label dump SE-PostgreSQL security labels\n")); ++ printf(_(" --security-acl dump row-level database ACLs\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); +@@ -1227,7 +1256,8 @@ dumpTableData_insert(Archive *fout, void *dcontext) + if (fout->remoteVersion >= 70100) + { + appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " +- "SELECT * FROM ONLY %s", ++ "SELECT %s* FROM ONLY %s", ++ (security_context > 0 ? "security_context, " : ""), + fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, + classname)); + } +@@ -1583,7 +1613,8 @@ dumpDatabase(Archive *AH) + i_collate, + i_ctype, + i_frozenxid, +- i_tablespace; ++ i_tablespace, ++ i_seclabel; + CatalogId dbCatId; + DumpId dbDumpId; + const char *datname, +@@ -1591,7 +1622,8 @@ dumpDatabase(Archive *AH) + *encoding, + *collate, + *ctype, +- *tablespace; ++ *tablespace, ++ *seclabel; + uint32 frozenxid; + + datname = PQdb(g_conn); +@@ -1610,11 +1642,12 @@ dumpDatabase(Archive *AH) + "pg_encoding_to_char(encoding) AS encoding, " + "datcollate, datctype, datfrozenxid, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " +- "shobj_description(oid, 'pg_database') AS description " +- ++ "shobj_description(oid, 'pg_database') AS description, " ++ "%s as security_context " + "FROM pg_database " + "WHERE datname = ", +- username_subquery); ++ username_subquery, ++ security_context ? "security_context" : "NULL"); + appendStringLiteralAH(dbQry, datname, AH); + } + else if (g_fout->remoteVersion >= 80200) +@@ -1624,8 +1657,8 @@ dumpDatabase(Archive *AH) + "pg_encoding_to_char(encoding) AS encoding, " + "NULL AS datcollate, NULL AS datctype, datfrozenxid, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " +- "shobj_description(oid, 'pg_database') AS description " +- ++ "shobj_description(oid, 'pg_database') AS description, " ++ "NULL as security_context " + "FROM pg_database " + "WHERE datname = ", + username_subquery); +@@ -1637,7 +1670,8 @@ dumpDatabase(Archive *AH) + "(%s datdba) AS dba, " + "pg_encoding_to_char(encoding) AS encoding, " + "NULL AS datcollate, NULL AS datctype, datfrozenxid, " +- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace " ++ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " ++ "NULL as security_context " + "FROM pg_database " + "WHERE datname = ", + username_subquery); +@@ -1650,7 +1684,8 @@ dumpDatabase(Archive *AH) + "pg_encoding_to_char(encoding) AS encoding, " + "NULL AS datcollate, NULL AS datctype, " + "0 AS datfrozenxid, " +- "NULL AS tablespace " ++ "NULL AS tablespace, " ++ "NULL AS security_context " + "FROM pg_database " + "WHERE datname = ", + username_subquery); +@@ -1665,7 +1700,8 @@ dumpDatabase(Archive *AH) + "pg_encoding_to_char(encoding) AS encoding, " + "NULL AS datcollate, NULL AS datctype, " + "0 AS datfrozenxid, " +- "NULL AS tablespace " ++ "NULL AS tablespace, " ++ "NULL as security_context " + "FROM pg_database " + "WHERE datname = ", + username_subquery); +@@ -1699,6 +1735,7 @@ dumpDatabase(Archive *AH) + i_ctype = PQfnumber(res, "datctype"); + i_frozenxid = PQfnumber(res, "datfrozenxid"); + i_tablespace = PQfnumber(res, "tablespace"); ++ i_seclabel = PQfnumber(res, "security_context"); + + dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid)); + dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid)); +@@ -1708,6 +1745,7 @@ dumpDatabase(Archive *AH) + ctype = PQgetvalue(res, 0, i_ctype); + frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid)); + tablespace = PQgetvalue(res, 0, i_tablespace); ++ seclabel = PQgetvalue(res, 0, i_seclabel); + + appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0", + fmtId(datname)); +@@ -1729,6 +1767,9 @@ dumpDatabase(Archive *AH) + if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0) + appendPQExpBuffer(creaQry, " TABLESPACE = %s", + fmtId(tablespace)); ++ if (strlen(seclabel) > 0) ++ appendPQExpBuffer(creaQry, " SECURITY_CONTEXT = '%s'", seclabel); ++ + appendPQExpBuffer(creaQry, ";\n"); + + if (binary_upgrade) +@@ -3230,6 +3271,7 @@ getTables(int *numTables) + int i_reltablespace; + int i_reloptions; + int i_toastreloptions; ++ int i_relseclabel; + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); +@@ -3271,7 +3313,8 @@ getTables(int *numTables) + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " +- "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " ++ "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, " ++ "%s as security_context " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3282,6 +3325,7 @@ getTables(int *numTables) + "WHERE c.relkind in ('%c', '%c', '%c', '%c') " + "ORDER BY c.oid", + username_subquery, ++ security_context ? "c.security_context" : "NULL", + RELKIND_SEQUENCE, + RELKIND_RELATION, RELKIND_SEQUENCE, + RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); +@@ -3303,7 +3347,8 @@ getTables(int *numTables) + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " +- "NULL AS toast_reloptions " ++ "NULL AS toast_reloptions, " ++ "NULL as security_context " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3334,7 +3379,8 @@ getTables(int *numTables) + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "NULL AS reloptions, " +- "NULL AS toast_reloptions " ++ "NULL AS toast_reloptions, " ++ "NULL as security_context " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3365,7 +3411,8 @@ getTables(int *numTables) + "d.refobjsubid AS owning_col, " + "NULL AS reltablespace, " + "NULL AS reloptions, " +- "NULL AS toast_reloptions " ++ "NULL AS toast_reloptions, " ++ "NULL as security_context " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3392,7 +3439,8 @@ getTables(int *numTables) + "NULL::int4 AS owning_col, " + "NULL AS reltablespace, " + "NULL AS reloptions, " +- "NULL AS toast_reloptions " ++ "NULL AS toast_reloptions, " ++ "NULL AS security_context " + "FROM pg_class " + "WHERE relkind IN ('%c', '%c', '%c') " + "ORDER BY oid", +@@ -3414,7 +3462,8 @@ getTables(int *numTables) + "NULL::int4 AS owning_col, " + "NULL AS reltablespace, " + "NULL AS reloptions, " +- "NULL AS toast_reloptions " ++ "NULL AS toast_reloptions, " ++ "NULL AS security_context " + "FROM pg_class " + "WHERE relkind IN ('%c', '%c', '%c') " + "ORDER BY oid", +@@ -3446,7 +3495,8 @@ getTables(int *numTables) + "NULL::int4 AS owning_col, " + "NULL AS reltablespace, " + "NULL AS reloptions, " +- "NULL AS toast_reloptions " ++ "NULL AS toast_reloptions, " ++ "NULL as security_context " + "FROM pg_class c " + "WHERE relkind IN ('%c', '%c') " + "ORDER BY oid", +@@ -3491,6 +3541,7 @@ getTables(int *numTables) + i_reltablespace = PQfnumber(res, "reltablespace"); + i_reloptions = PQfnumber(res, "reloptions"); + i_toastreloptions = PQfnumber(res, "toast_reloptions"); ++ i_relseclabel = PQfnumber(res, "security_context"); + + if (lockWaitTimeout && g_fout->remoteVersion >= 70300) + { +@@ -3538,6 +3589,7 @@ getTables(int *numTables) + tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace)); + tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions)); + tblinfo[i].toast_reloptions = strdup(PQgetvalue(res, i, i_toastreloptions)); ++ tblinfo[i].relseclabel = strdup(PQgetvalue(res, i, i_relseclabel)); + + /* other fields were zeroed above */ + +@@ -4737,6 +4789,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + int i_attlen; + int i_attalign; + int i_attislocal; ++ int i_attseclabel; + PGresult *res; + int ntups; + bool hasdefaults; +@@ -4781,12 +4834,14 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + "a.attstattarget, a.attstorage, t.typstorage, " + "a.attnotnull, a.atthasdef, a.attisdropped, " + "a.attlen, a.attalign, a.attislocal, " +- "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname " ++ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " ++ "%s as security_context " + "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::pg_catalog.oid " + "AND a.attnum > 0::pg_catalog.int2 " + "ORDER BY a.attrelid, a.attnum", ++ security_context ? "a.security_context" : "NULL", + tbinfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 70100) +@@ -4801,7 +4856,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + "t.typstorage, a.attnotnull, a.atthasdef, " + "false AS attisdropped, a.attlen, " + "a.attalign, false AS attislocal, " +- "format_type(t.oid,a.atttypmod) AS atttypname " ++ "format_type(t.oid,a.atttypmod) AS atttypname, " ++ "NULL as security_context " + "FROM pg_attribute a LEFT JOIN pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::oid " +@@ -4818,7 +4874,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + "attnotnull, atthasdef, false AS attisdropped, " + "attlen, attalign, " + "false AS attislocal, " +- "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname " ++ "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, " ++ "NULL as security_context " + "FROM pg_attribute a " + "WHERE attrelid = '%u'::oid " + "AND attnum > 0::int2 " +@@ -4844,6 +4901,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + i_attlen = PQfnumber(res, "attlen"); + i_attalign = PQfnumber(res, "attalign"); + i_attislocal = PQfnumber(res, "attislocal"); ++ i_attseclabel = PQfnumber(res, "security_context"); + + tbinfo->numatts = ntups; + tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); +@@ -4856,6 +4914,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + tbinfo->attlen = (int *) malloc(ntups * sizeof(int)); + tbinfo->attalign = (char *) malloc(ntups * sizeof(char)); + tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool)); ++ tbinfo->attseclabel = (char **) malloc(ntups * sizeof(char *)); + tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool)); + tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *)); + tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); +@@ -4881,6 +4940,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen)); + tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign)); + tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't'); ++ tbinfo->attseclabel[j] = strdup(PQgetvalue(res, j, i_attseclabel)); + tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't'); + tbinfo->attrdefs[j] = NULL; /* fix below */ + if (PQgetvalue(res, j, i_atthasdef)[0] == 't') +@@ -7131,6 +7191,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + char *proconfig; + char *procost; + char *prorows; ++ char *proseclabel; + char *lanname; + char *rettypename; + int nallargs; +@@ -7167,9 +7228,11 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "pg_catalog.pg_get_function_result(oid) AS funcresult, " + "proiswindow, provolatile, proisstrict, prosecdef, " + "proconfig, procost, prorows, " +- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " ++ "%s as security_context " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", ++ security_context ? "security_context" : "NULL", + finfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 80300) +@@ -7180,7 +7243,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "false AS proiswindow, " + "provolatile, proisstrict, prosecdef, " + "proconfig, procost, prorows, " +- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " ++ "NULL AS security_context " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +@@ -7193,7 +7257,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "false AS proiswindow, " + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " +- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " ++ "NULL AS security_context " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +@@ -7208,7 +7273,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "false AS proiswindow, " + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " +- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " ++ "NULL AS security_context " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +@@ -7223,7 +7289,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "false AS proiswindow, " + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " +- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " ++ "NULL AS security_context " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +@@ -7240,7 +7307,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "proisstrict, " + "false AS prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " +- "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " ++ "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname, " ++ "NULL AS security_context " + "FROM pg_proc " + "WHERE oid = '%u'::oid", + finfo->dobj.catId.oid); +@@ -7257,7 +7325,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "false AS proisstrict, " + "false AS prosecdef, " + "NULL AS proconfig, 0 AS procost, 0 AS prorows, " +- "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " ++ "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname, " ++ "NULL AS security_context " + "FROM pg_proc " + "WHERE oid = '%u'::oid", + finfo->dobj.catId.oid); +@@ -7301,6 +7370,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig")); + procost = PQgetvalue(res, 0, PQfnumber(res, "procost")); + prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); ++ proseclabel = PQgetvalue(res, 0, PQfnumber(res, "security_context")); + lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); + + /* +@@ -7459,6 +7529,9 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + if (prosecdef[0] == 't') + appendPQExpBuffer(q, " SECURITY DEFINER"); + ++ if (security_context > 0 && strlen(proseclabel) > 0) ++ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", proseclabel); ++ + /* + * COST and ROWS are emitted only if present and not default, so as not to + * break backwards-compatibility of the dump without need. Keep this code +@@ -9917,6 +9990,17 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) + if (tbinfo->notnull[j] && + (!tbinfo->inhNotNull[j] || binary_upgrade)) + appendPQExpBuffer(q, " NOT NULL"); ++ ++ /* ++ * Security label -- if SE-PostgreSQL enabled ++ */ ++ if (security_context > 0 && ++ strlen(tbinfo->attseclabel[j]) > 0 && ++ strcmp(tbinfo->relseclabel, tbinfo->attseclabel[j]) != 0) ++ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", ++ tbinfo->attseclabel[j]); ++ ++ actual_atts++; + } + } + +@@ -9979,6 +10063,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) + appendPQExpBuffer(q, ")"); + } + ++ if (security_context > 0 && strlen(tbinfo->relseclabel) > 0) ++ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", tbinfo->relseclabel); ++ + appendPQExpBuffer(q, ";\n"); + + /* +@@ -11550,6 +11637,13 @@ fmtCopyColumnList(const TableInfo *ti) + + appendPQExpBuffer(q, "("); + needComma = false; ++ ++ if (security_context > 0) ++ { ++ appendPQExpBuffer(q, "security_context"); ++ needComma = true; ++ } ++ + for (i = 0; i < numatts; i++) + { + if (attisdropped[i]) +diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h +index a9b3dae..350025c 100644 +--- a/src/bin/pg_dump/pg_dump.h ++++ b/src/bin/pg_dump/pg_dump.h +@@ -228,6 +228,7 @@ typedef struct _tableInfo + bool hasoids; /* does it have OIDs? */ + uint32 frozenxid; /* for restore frozen xid */ + int ncheck; /* # of CHECK expressions */ ++ char *relseclabel; /* security labels of relation */ + /* these two are set only if table is a sequence owned by a column: */ + Oid owning_tab; /* OID of table owning sequence */ + int owning_col; /* attr # of column owning sequence */ +@@ -249,6 +250,7 @@ typedef struct _tableInfo + int *attlen; /* attribute length, used by binary_upgrade */ + char *attalign; /* attribute align, used by binary_upgrade */ + bool *attislocal; /* true if attr has local definition */ ++ char **attseclabel; /* security labels of attributes */ + + /* + * Note: we need to store per-attribute notnull, default, and constraint +diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c +index b265398..5011551 100644 +--- a/src/bin/pg_dump/pg_dumpall.c ++++ b/src/bin/pg_dump/pg_dumpall.c +@@ -69,6 +69,9 @@ static int no_tablespaces = 0; + static int use_setsessauth = 0; + static int server_version; + ++static int security_label = 0; ++static int security_acl = 0; ++ + static FILE *OPF; + static char *filename = NULL; + +@@ -130,6 +133,8 @@ main(int argc, char *argv[]) + {"no-tablespaces", no_argument, &no_tablespaces, 1}, + {"role", required_argument, NULL, 3}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"security-label", no_argument, &security_label, 1}, ++ {"security-acl", no_argument, &security_acl, 1}, + + {NULL, 0, NULL, 0} + }; +@@ -283,6 +288,10 @@ main(int argc, char *argv[]) + no_tablespaces = 1; + else if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; ++ else if (strcmp(optarg, "security-label") == 0) ++ security_label = 1; ++ else if (strcmp(optarg, "security-acl") == 0) ++ security_acl = 1; + else + { + fprintf(stderr, +@@ -328,6 +337,10 @@ main(int argc, char *argv[]) + appendPQExpBuffer(pgdumpopts, " --no-tablespaces"); + if (use_setsessauth) + appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); ++ if (security_label) ++ appendPQExpBuffer(pgdumpopts, " --security-label"); ++ if (security_acl) ++ appendPQExpBuffer(pgdumpopts, " --security-acl"); + + if (optind < argc) + { +@@ -403,6 +416,19 @@ main(int argc, char *argv[]) + } + } + ++ if (security_label > 0) ++ { ++ PGresult *res ++ = PQexec(conn, "SHOW sepostgresql"); ++ if (PQresultStatus(res) != PGRES_TUPLES_OK || ++ PQntuples(res) != 1 || ++ strcmp(PQgetvalue(res, 0, 0), "on") != 0) ++ { ++ fprintf(stderr, "SE-PostgreSQL is not available now."); ++ exit(1); ++ } ++ } ++ + /* + * Open the output file if required, otherwise use stdout + */ +@@ -1130,55 +1156,56 @@ dumpCreateDB(PGconn *conn) + + /* Now collect all the information about databases to dump */ + if (server_version >= 80400) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "datcollate, datctype, datfrozenxid, " + "datistemplate, datacl, datconnlimit, " +- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " ++ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " ++ "%s AS security_label " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " +- "WHERE datallowconn ORDER BY 1"); ++ "WHERE datallowconn ORDER BY 1", ++ security_label ? "sepgsql_raw_to_trans(datselabel)" : "null::text"); + else if (server_version >= 80100) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, datconnlimit, " +- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " ++ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " ++ "null::text " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 80000) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, -1 as datconnlimit, " +- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " ++ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " ++ "null::text " + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70300) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, -1 as datconnlimit, " +- "'pg_default' AS dattablespace " ++ "'pg_default' AS dattablespace, " ++ "null::text " + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70100) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(" + "(select usename from pg_shadow where usesysid=datdba), " + "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " + "datistemplate, '' as datacl, -1 as datconnlimit, " +- "'pg_default' AS dattablespace " ++ "'pg_default' AS dattablespace, " ++ "null::text " + "FROM pg_database d " + "WHERE datallowconn ORDER BY 1"); + else +@@ -1187,18 +1214,20 @@ dumpCreateDB(PGconn *conn) + * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal + * with getting a NULL by not printing any OWNER clause. + */ +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "(select usename from pg_shadow where usesysid=datdba), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " + "'f' as datistemplate, " + "'' as datacl, -1 as datconnlimit, " +- "'pg_default' AS dattablespace " ++ "'pg_default' AS dattablespace, " ++ "null::text " + "FROM pg_database d " + "ORDER BY 1"); + } + ++ res = PQexec(conn, buf->data); ++ + for (i = 0; i < PQntuples(res); i++) + { + char *dbname = PQgetvalue(res, i, 0); +@@ -1211,6 +1240,7 @@ dumpCreateDB(PGconn *conn) + char *dbacl = PQgetvalue(res, i, 7); + char *dbconnlimit = PQgetvalue(res, i, 8); + char *dbtablespace = PQgetvalue(res, i, 9); ++ char *dbseclabel = PQgetvalue(res, i, 9); + char *fdbname; + + fdbname = strdup(fmtId(dbname)); +@@ -1266,6 +1296,10 @@ dumpCreateDB(PGconn *conn) + appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", + dbconnlimit); + ++ if (security_label > 0 && strlen(dbseclabel) > 0) ++ appendPQExpBuffer(buf, " SECURITY_LABEL = '%s'", ++ dbseclabel); ++ + appendPQExpBuffer(buf, ";\n"); + + if (strcmp(dbistemplate, "t") == 0) +diff --git a/src/include/access/htup.h b/src/include/access/htup.h +index f271cbc..adf12c0 100644 +--- a/src/include/access/htup.h ++++ b/src/include/access/htup.h +@@ -163,7 +163,7 @@ typedef HeapTupleHeaderData *HeapTupleHeader; + #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ + #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ + #define HEAP_HASOID 0x0008 /* has an object-id field */ +-/* bit 0x0010 is available */ ++#define HEAP_HASSECID 0x0010 /* has an security-id field */ + #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ + #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ + #define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */ +@@ -290,6 +290,9 @@ do { \ + (tup)->t_choice.t_datum.datum_typmod = (typmod) \ + ) + ++#define HeapTupleHeaderHasOid(tup) \ ++ ((tup)->t_infomask & HEAP_HASOID) ++ + #define HeapTupleHeaderGetOid(tup) \ + ( \ + ((tup)->t_infomask & HEAP_HASOID) ? \ +@@ -349,6 +352,25 @@ do { \ + (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ + ) + ++#define HeapTupleHeaderHasSecid(tup) \ ++ ((tup)->t_infomask & HEAP_HASSECID) ++ ++#define HeapTupleHeaderGetSecid(tup) \ ++ ( \ ++ HeapTupleHeaderHasSecid(tup) \ ++ ? (*(Oid *)((char *)(tup) + (tup)->t_hoff \ ++ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ ++ - sizeof(Oid))) \ ++ : InvalidOid \ ++ ) ++ ++#define HeapTupleHeaderSetSecid(tup, secid) \ ++ do { \ ++ Assert(HeapTupleHeaderHasSecid(tup)); \ ++ *((Oid *)((char *)(tup) + (tup)->t_hoff \ ++ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ ++ - sizeof(Oid))) = (secid); \ ++ } while(0) + + /* + * BITMAPLEN(NATTS) - +@@ -549,6 +571,14 @@ typedef HeapTupleData *HeapTuple; + #define HeapTupleSetOid(tuple, oid) \ + HeapTupleHeaderSetOid((tuple)->t_data, (oid)) + ++#define HeapTupleHasSecid(tuple) \ ++ HeapTupleHeaderHasSecid((tuple)->t_data) ++ ++#define HeapTupleGetSecid(tuple) \ ++ HeapTupleHeaderGetSecid((tuple)->t_data) ++ ++#define HeapTupleSetSecid(tuple, secid) \ ++ HeapTupleHeaderSetSecid((tuple)->t_data, (secid)) + + /* + * WAL record definitions for heapam.c's WAL operations +diff --git a/src/include/access/sysattr.h b/src/include/access/sysattr.h +index f8fa910..762219f 100644 +--- a/src/include/access/sysattr.h ++++ b/src/include/access/sysattr.h +@@ -25,7 +25,19 @@ + #define MaxTransactionIdAttributeNumber (-5) + #define MaxCommandIdAttributeNumber (-6) + #define TableOidAttributeNumber (-7) +-#define FirstLowInvalidHeapAttributeNumber (-8) ++#define SecurityAttributeNumber (-8) ++#define FirstLowInvalidHeapAttributeNumber (-9) + ++/* ++ * Attribute names for the system-defined attributes ++ */ ++#define SelfItemPointerAttributeName "ctid" ++#define ObjectIdAttributeName "oid" ++#define MinTransactionIdAttributeName "xmin" ++#define MinCommandIdAttributeName "cmin" ++#define MaxTransactionIdAttributeName "xmax" ++#define MaxCommandIdAttributeName "cmax" ++#define TableOidAttributeName "tableoid" ++#define SecurityAttributeName "security_context" + + #endif /* SYSATTR_H */ +diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h +index 87c931a..b102e90 100644 +--- a/src/include/access/tupdesc.h ++++ b/src/include/access/tupdesc.h +@@ -75,6 +75,7 @@ typedef struct tupleDesc + Oid tdtypeid; /* composite type ID for tuple type */ + int32 tdtypmod; /* typmod for tuple type */ + bool tdhasoid; /* tuple has oid attribute in its header */ ++ bool tdhassecid; /* tuple has secid attribute in its header */ + int tdrefcount; /* reference count, or -1 if not counting */ + } *TupleDesc; + +diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h +index b43408f..c4342b2 100644 +--- a/src/include/bootstrap/bootstrap.h ++++ b/src/include/bootstrap/bootstrap.h +@@ -70,7 +70,8 @@ typedef enum + BootstrapProcess, + StartupProcess, + BgWriterProcess, +- WalWriterProcess ++ WalWriterProcess, ++ SelinuxReceiverProcess, + } AuxProcType; + + #endif /* BOOTSTRAP_H */ +diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h +index 62b9a52..5c008b9 100644 +--- a/src/include/catalog/dependency.h ++++ b/src/include/catalog/dependency.h +@@ -156,6 +156,9 @@ typedef enum ObjectClass + extern void performDeletion(const ObjectAddress *object, + DropBehavior behavior); + ++extern void performDeletionNoPerms(const ObjectAddress *object, ++ DropBehavior behavior); ++ + extern void performMultipleDeletions(const ObjectAddresses *objects, + DropBehavior behavior); + +diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h +index d618319..e660545 100644 +--- a/src/include/catalog/heap.h ++++ b/src/include/catalog/heap.h +@@ -56,7 +56,8 @@ extern Oid heap_create_with_catalog(const char *relname, + int oidinhcount, + OnCommitAction oncommit, + Datum reloptions, +- bool allow_system_table_mods); ++ bool allow_system_table_mods, ++ Oid *secLabels); + + extern void heap_drop_with_catalog(Oid relid); + +@@ -68,12 +69,14 @@ extern List *heap_truncate_find_FKs(List *relationIds); + + extern void InsertPgAttributeTuple(Relation pg_attribute_rel, + Form_pg_attribute new_attribute, +- CatalogIndexState indstate); ++ CatalogIndexState indstate, ++ Oid new_att_secid); + + extern void InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, +- Datum reloptions); ++ Datum reloptions, ++ Oid new_rel_secid); + + extern List *AddRelationNewConstraints(Relation rel, + List *newColDefaults, +@@ -103,6 +106,8 @@ extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno, + extern Form_pg_attribute SystemAttributeByName(const char *attname, + bool relhasoids); + ++extern bool SystemAttributeIsWritable(AttrNumber attnum); ++ + extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind); + + extern void CheckAttributeType(const char *attname, Oid atttypid); +diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h +index 0a46611..70024b1 100644 +--- a/src/include/catalog/indexing.h ++++ b/src/include/catalog/indexing.h +@@ -252,6 +252,11 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index, 2703, on pg_type using btree(oid oid_ops + DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); + #define TypeNameNspIndexId 2704 + ++DECLARE_UNIQUE_INDEX(pg_security_secid_index, 3401, on pg_security using btree(secid oid_ops, datid oid_ops, relid oid_ops)); ++#define SecuritySecidIndexId 3401 ++DECLARE_INDEX(pg_security_secattr_index, 3402, on pg_security using btree(datid oid_ops, relid oid_ops, secattr text_ops)); ++#define SecuritySecattrIndexId 3402 ++ + DECLARE_UNIQUE_INDEX(pg_foreign_data_wrapper_oid_index, 112, on pg_foreign_data_wrapper using btree(oid oid_ops)); + #define ForeignDataWrapperOidIndexId 112 + +diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h +index eaa405f..165b54d 100644 +--- a/src/include/catalog/pg_attribute.h ++++ b/src/include/catalog/pg_attribute.h +@@ -276,6 +276,7 @@ DATA(insert ( 1247 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); ++DATA(insert ( 1247 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); + + /* ---------------- + * pg_proc +@@ -340,6 +341,7 @@ DATA(insert ( 1255 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); ++DATA(insert ( 1255 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); + + /* ---------------- + * pg_attribute +@@ -390,6 +392,7 @@ DATA(insert ( 1249 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); ++DATA(insert ( 1249 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); + + /* ---------------- + * pg_class +@@ -454,6 +457,7 @@ DATA(insert ( 1259 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); ++DATA(insert ( 1259 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); + + /* ---------------- + * pg_index +diff --git a/src/include/catalog/pg_conversion_fn.h b/src/include/catalog/pg_conversion_fn.h +index 83cf657..eecebd3 100644 +--- a/src/include/catalog/pg_conversion_fn.h ++++ b/src/include/catalog/pg_conversion_fn.h +@@ -17,7 +17,7 @@ + extern Oid ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +- Oid conproc, bool def); ++ Oid conproc, Oid consecid, bool def); + extern void RemoveConversionById(Oid conversionOid); + extern Oid FindConversion(const char *conname, Oid connamespace); + extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); +diff --git a/src/include/catalog/pg_largeobject.h b/src/include/catalog/pg_largeobject.h +index 6dd2fb0..b84c0d2 100644 +--- a/src/include/catalog/pg_largeobject.h ++++ b/src/include/catalog/pg_largeobject.h +@@ -51,7 +51,7 @@ typedef FormData_pg_largeobject *Form_pg_largeobject; + #define Anum_pg_largeobject_pageno 2 + #define Anum_pg_largeobject_data 3 + +-extern Oid LargeObjectCreate(Oid loid); ++extern Oid LargeObjectCreate(Oid loid, Oid secid); + extern void LargeObjectDrop(Oid loid); + extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId); + extern bool LargeObjectExists(Oid loid); +diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h +index 9168079..7e8487a 100644 +--- a/src/include/catalog/pg_namespace.h ++++ b/src/include/catalog/pg_namespace.h +@@ -77,6 +77,6 @@ DESCR("standard public schema"); + /* + * prototypes for functions in pg_namespace.c + */ +-extern Oid NamespaceCreate(const char *nspName, Oid ownerId); ++extern Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid nspsecid); + + #endif /* PG_NAMESPACE_H */ +diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h +index 0285acd..c6a5247 100644 +--- a/src/include/catalog/pg_proc.h ++++ b/src/include/catalog/pg_proc.h +@@ -4335,6 +4335,19 @@ DESCR("I/O"); + DATA(insert OID = 2963 ( uuid_hash PGNSP PGUID 12 1 0 0 f f f t f i 1 0 23 "2950" _null_ _null_ _null_ _null_ uuid_hash _null_ _null_ _null_ )); + DESCR("hash"); + ++/* SE-PostgreSQL related functions */ ++DATA(insert OID = 3415 ( seclabel_to_secid PGNSP PGUID 12 1 0 0 f f f t f v 1 0 26 "2249" _null_ _null_ _null_ _null_ seclabel_to_secid _null_ _null_ _null_ )); ++DATA(insert OID = 3416 ( sepgsql_getcon PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ sepgsql_getcon _null_ _null_ _null_ )); ++DATA(insert OID = 3417 ( sepgsql_server_getcon PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ sepgsql_server_getcon _null_ _null_ _null_ )); ++DATA(insert OID = 3418 ( sepgsql_get_user PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_user _null_ _null_ _null_ )); ++DATA(insert OID = 3419 ( sepgsql_set_user PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_user _null_ _null_ _null_ )); ++DATA(insert OID = 3420 ( sepgsql_get_role PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_role _null_ _null_ _null_ )); ++DATA(insert OID = 3421 ( sepgsql_set_role PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_role _null_ _null_ _null_ )); ++DATA(insert OID = 3422 ( sepgsql_get_type PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_type _null_ _null_ _null_ )); ++DATA(insert OID = 3423 ( sepgsql_set_type PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_type _null_ _null_ _null_ )); ++DATA(insert OID = 3424 ( sepgsql_get_range PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_range _null_ _null_ _null_ )); ++DATA(insert OID = 3425 ( sepgsql_set_range PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_range _null_ _null_ _null_ )); ++ + /* enum related procs */ + DATA(insert OID = 3504 ( anyenum_in PGNSP PGUID 12 1 0 0 f f f t f i 1 0 3500 "2275" _null_ _null_ _null_ _null_ anyenum_in _null_ _null_ _null_ )); + DESCR("I/O"); +diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h +index e3453f2..c25af6b 100644 +--- a/src/include/catalog/pg_proc_fn.h ++++ b/src/include/catalog/pg_proc_fn.h +@@ -37,7 +37,8 @@ extern Oid ProcedureCreate(const char *procedureName, + List *parameterDefaults, + Datum proconfig, + float4 procost, +- float4 prorows); ++ float4 prorows, ++ Node *proseclabel); + + extern bool function_parse_error_transpose(const char *prosrc); + +diff --git a/src/include/catalog/pg_security.h b/src/include/catalog/pg_security.h +new file mode 100644 +index 0000000..973df01 +--- /dev/null ++++ b/src/include/catalog/pg_security.h +@@ -0,0 +1,89 @@ ++/* ++ * src/include/catalog/pg_security.h ++ * Definition of the security label relation (pg_security) ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#ifndef PG_SECURITY_H ++#define PG_SECURITY_H ++ ++#include "catalog/genbki.h" ++ ++#include "access/htup.h" ++#include "nodes/parsenodes.h" ++#include "utils/acl.h" ++#include "utils/relcache.h" ++ ++#define SecurityRelationId 3400 ++ ++CATALOG(pg_security,3400) BKI_SHARED_RELATION BKI_WITHOUT_OIDS ++{ ++ /* Identifier of the security attribute */ ++ Oid secid; ++ ++ /* OID of the database which referes the entry */ ++ Oid datid; ++ ++ /* OID of the table which refers the entry */ ++ Oid relid; ++ ++ /* Text representation of security attribute */ ++ text secattr; ++} FormData_pg_security; ++ ++/* ++ * Form_pg_security corresponds to a pointer to a tuple with ++ * the format of pg_security relation. ++ */ ++typedef FormData_pg_security *Form_pg_security; ++ ++/* ++ * Compiler constants for pg_security ++ */ ++#define Natts_pg_security 4 ++#define Anum_pg_security_secid 1 ++#define Anum_pg_security_datid 2 ++#define Anum_pg_security_relid 3 ++#define Anum_pg_security_secattr 4 ++ ++/* ++ * Functions to translate between security label and identifier ++ */ ++extern void ++securityPostBootstrapingMode(void); ++ ++extern void ++securityOnCreateDatabase(Oid src_datid, Oid dst_datid); ++ ++extern void ++securityOnDropDatabase(Oid datid); ++ ++extern bool ++securityTupleDescHasSecid(Oid relid, char relkind); ++ ++extern Oid ++securityRawSecLabelIn(Oid relid, char *seclabel); ++ ++extern char * ++securityRawSecLabelOut(Oid relid, Oid secid); ++ ++extern Oid ++securityTransSecLabelIn(Oid relid, char *seclabel); ++ ++extern char * ++securityTransSecLabelOut(Oid relid, Oid secid); ++ ++extern Datum ++securitySysattSecLabelOut(Oid relid, HeapTuple tuple); ++ ++extern void ++securityReclaimOnDropTable(Oid relid); ++ ++extern void ++seclabelRelationReclaim(Oid relOid); ++ ++extern Datum ++seclabel_to_secid(PG_FUNCTION_ARGS); ++ ++#endif /* PG_SECURITY_H */ +diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h +index bd6e0cf..b7c39f0 100644 +--- a/src/include/catalog/toasting.h ++++ b/src/include/catalog/toasting.h +@@ -58,5 +58,8 @@ DECLARE_TOAST(pg_database, 2844, 2845); + DECLARE_TOAST(pg_shdescription, 2846, 2847); + #define PgShdescriptionToastTable 2846 + #define PgShdescriptionToastIndex 2847 ++DECLARE_TOAST(pg_security, 3403, 3404); ++#define PgSecurityToastTable 3403 ++#define PgSecurityToastIndex 3404 + + #endif /* TOASTING_H */ +diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h +index ab5d6f4..c332b71 100644 +--- a/src/include/commands/alter.h ++++ b/src/include/commands/alter.h +@@ -19,5 +19,6 @@ + extern void ExecRenameStmt(RenameStmt *stmt); + extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); + extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); ++extern void ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt); + + #endif /* ALTER_H */ +diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h +index 0ec23d7..3980056 100644 +--- a/src/include/commands/dbcommands.h ++++ b/src/include/commands/dbcommands.h +@@ -58,6 +58,7 @@ extern void RenameDatabase(const char *oldname, const char *newname); + extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel); + extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt); + extern void AlterDatabaseOwner(const char *dbname, Oid newOwnerId); ++extern void AlterDatabaseSecLabel(const char *dbname, DefElem *seclabel); + + extern Oid get_database_oid(const char *dbname); + extern char *get_database_name(Oid dbid); +diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h +index 4356492..8f903af 100644 +--- a/src/include/commands/defrem.h ++++ b/src/include/commands/defrem.h +@@ -53,6 +53,7 @@ extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType); + extern void RenameFunction(List *name, List *argtypes, const char *newname); + extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId); + extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId); ++extern void AlterFunctionSecLabel(List *name, List *argtypes, DefElem *seclabel); + extern void AlterFunction(AlterFunctionStmt *stmt); + extern void CreateCast(CreateCastStmt *stmt); + extern void DropCast(DropCastStmt *stmt); +diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h +index 5f384a1..1fc113b 100644 +--- a/src/include/commands/schemacmds.h ++++ b/src/include/commands/schemacmds.h +@@ -26,5 +26,6 @@ extern void RemoveSchemaById(Oid schemaOid); + extern void RenameSchema(const char *oldname, const char *newname); + extern void AlterSchemaOwner(const char *name, Oid newOwnerId); + extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId); ++extern void AlterSchemaSecLabel(const char *name, DefElem *seclabel); + + #endif /* SCHEMACMDS_H */ +diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h +index 21b067c..2ac7b3c 100644 +--- a/src/include/commands/tablecmds.h ++++ b/src/include/commands/tablecmds.h +@@ -35,6 +35,9 @@ extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, + Oid oldNspOid, Oid newNspOid, + bool hasDependEntry); + ++extern void AlterRelationSecLabel(RangeVar *relation, const char *attname, ++ ObjectType objtype, DefElem *seclabel); ++ + extern void CheckTableNotInUse(Relation rel, const char *stmt); + + extern void ExecuteTruncate(TruncateStmt *stmt); +diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h +index 43395e9..09d6148 100644 +--- a/src/include/executor/executor.h ++++ b/src/include/executor/executor.h +@@ -130,7 +130,7 @@ extern TupleHashEntry FindTupleHashEntry(TupleHashTable hashtable, + /* + * prototypes from functions in execJunk.c + */ +-extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, ++extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel, + TupleTableSlot *slot); + extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, + TupleDesc cleanTupType, +@@ -163,6 +163,7 @@ extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, + bool doInstrument); + extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); + extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); ++extern bool ExecContextForcesSecids(PlanState *planstate, bool *hassecids); + extern void ExecConstraints(ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, EState *estate); + extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, +@@ -216,8 +217,8 @@ extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate); + extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); + extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, + TupleDesc tupType); +-extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); +-extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); ++extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hasseclabel); ++extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hasseclabel); + extern TupleDesc ExecTypeFromExprList(List *exprList); + extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); + +diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h +index e40082d..7fd299c 100644 +--- a/src/include/executor/tuptable.h ++++ b/src/include/executor/tuptable.h +@@ -127,6 +127,7 @@ typedef struct TupleTableSlot + MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */ + HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */ + long tts_off; /* saved state for slot_deform_tuple */ ++ Datum tts_seclabel; /* temp storage for the given security_label */ + } TupleTableSlot; + + #define TTS_HAS_PHYSICAL_TUPLE(slot) \ +diff --git a/src/include/libpq/be-fsstubs.h b/src/include/libpq/be-fsstubs.h +index 862b014..03ca71b 100644 +--- a/src/include/libpq/be-fsstubs.h ++++ b/src/include/libpq/be-fsstubs.h +@@ -37,6 +37,9 @@ extern Datum lo_tell(PG_FUNCTION_ARGS); + extern Datum lo_unlink(PG_FUNCTION_ARGS); + extern Datum lo_truncate(PG_FUNCTION_ARGS); + ++extern Datum lo_get_security(PG_FUNCTION_ARGS); ++extern Datum lo_set_security(PG_FUNCTION_ARGS); ++ + /* + * compatibility option for access control + */ +diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h +index 925375b..0285a39 100644 +--- a/src/include/nodes/nodes.h ++++ b/src/include/nodes/nodes.h +@@ -337,6 +337,7 @@ typedef enum NodeTag + T_CreateUserMappingStmt, + T_AlterUserMappingStmt, + T_DropUserMappingStmt, ++ T_AlterSecLabelStmt, + + /* + * TAGS FOR PARSE TREE NODES (parsenodes.h) +diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h +index 487a226..cdb49d4 100644 +--- a/src/include/nodes/parsenodes.h ++++ b/src/include/nodes/parsenodes.h +@@ -463,6 +463,7 @@ typedef struct ColumnDef + Node *raw_default; /* default value (untransformed parse tree) */ + Node *cooked_default; /* default value (transformed expr tree) */ + List *constraints; /* other constraints on column */ ++ Node *secLabel; /* security label of column */ + } ColumnDef; + + /* +@@ -1069,6 +1070,7 @@ typedef struct CreateSchemaStmt + NodeTag type; + char *schemaname; /* the name of the schema to create */ + char *authid; /* the owner of the created schema */ ++ Node *secLabel; /* explicitly specified security label */ + List *schemaElts; /* schema components (list of parsenodes) */ + } CreateSchemaStmt; + +@@ -1335,6 +1337,7 @@ typedef struct CreateStmt + List *options; /* options from WITH clause */ + OnCommitAction oncommit; /* what do we do at COMMIT? */ + char *tablespacename; /* table space to use, or NULL */ ++ List *secLabel; /* explicitly specified security label */ + } CreateStmt; + + /* ---------- +@@ -1639,6 +1642,7 @@ typedef struct CreateSeqStmt + NodeTag type; + RangeVar *sequence; /* the sequence to create */ + List *options; ++ Node *secLabel; + } CreateSeqStmt; + + typedef struct AlterSeqStmt +@@ -1993,6 +1997,20 @@ typedef struct AlterOwnerStmt + char *newowner; /* the new owner */ + } AlterOwnerStmt; + ++/* ---------------------- ++ * Alter Object Security Label Statement ++ * ---------------------- ++ */ ++typedef struct AlterSecLabelStmt ++{ ++ NodeTag type; ++ ObjectType objectType; /* OBJECT_TABLE, OBJECT_COLUMN, etc */ ++ RangeVar *relation; /* in case it's a table */ ++ List *object; /* in case it's some other object */ ++ List *objarg; /* argument types, if applicable */ ++ char *subname; /* column name, if needed */ ++ Node *secLabel; /* the new security label */ ++} AlterSecLabelStmt; + + /* ---------------------- + * Create Rule Statement +diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h +index 23a5117..2525e22 100644 +--- a/src/include/nodes/plannodes.h ++++ b/src/include/nodes/plannodes.h +@@ -16,6 +16,7 @@ + + #include "access/sdir.h" + #include "nodes/bitmapset.h" ++#include "nodes/parsenodes.h" + #include "nodes/primnodes.h" + #include "storage/itemptr.h" + +@@ -239,6 +240,12 @@ typedef struct Scan + { + Plan plan; + Index scanrelid; /* relid is index into the range table */ ++ ++ /* ++ * Row-level access control stuff. Zero means we don't need ++ * to apply row-level access control on the Scan. ++ */ ++ uint32 rowlvPerms; + } Scan; + + /* ---------------- +diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h +index ea48889..6133c38 100644 +--- a/src/include/nodes/relation.h ++++ b/src/include/nodes/relation.h +@@ -383,6 +383,15 @@ typedef struct RelOptInfo + * list just to avoid recomputing the best inner indexscan repeatedly for + * similar outer relations. See comments for InnerIndexscanInfo. + */ ++ ++ /* ++ * Permissions used in Row-level access control features both of DAC ++ * and MAC. The lower 16bit is used for DAC, and rest of upper bits ++ * are used for MAC. When rowlvPerms is zero, so it means we don't need ++ * to apply the row-level stuff on the relation in both of levels. ++ * It can be used as a hint for optimization stuff. ++ */ ++ uint32 rowlvPerms; + } RelOptInfo; + + /* +diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h +index 67e9cb4..df9bb5b 100644 +--- a/src/include/parser/kwlist.h ++++ b/src/include/parser/kwlist.h +@@ -88,6 +88,7 @@ PG_KEYWORD("connection", CONNECTION, UNRESERVED_KEYWORD) + PG_KEYWORD("constraint", CONSTRAINT, RESERVED_KEYWORD) + PG_KEYWORD("constraints", CONSTRAINTS, UNRESERVED_KEYWORD) + PG_KEYWORD("content", CONTENT_P, UNRESERVED_KEYWORD) ++PG_KEYWORD("context", CONTEXT_P, UNRESERVED_KEYWORD) + PG_KEYWORD("continue", CONTINUE_P, UNRESERVED_KEYWORD) + PG_KEYWORD("conversion", CONVERSION_P, UNRESERVED_KEYWORD) + PG_KEYWORD("copy", COPY, UNRESERVED_KEYWORD) +diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in +index 3473227..28301d0 100644 +--- a/src/include/pg_config.h.in ++++ b/src/include/pg_config.h.in +@@ -263,6 +263,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LDAP_H + ++/* Define to 1 if you have the `audit' library (-laudit). */ ++#undef HAVE_LIBAUDIT ++ + /* Define to 1 if you have the `crypto' library (-lcrypto). */ + #undef HAVE_LIBCRYPTO + +@@ -391,6 +394,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_SECURITY_PAM_APPL_H + ++/* Define to 1 if you enable SELinux support */ ++#undef HAVE_SELINUX ++ + /* Define to 1 if you have the `setproctitle' function. */ + #undef HAVE_SETPROCTITLE + +diff --git a/src/include/security/rowlevel.h b/src/include/security/rowlevel.h +new file mode 100644 +index 0000000..a737a0d +--- /dev/null ++++ b/src/include/security/rowlevel.h +@@ -0,0 +1,44 @@ ++/* ++ * src/include/security/rowlevel.h ++ * Definition of the facility of row-level access controls ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#ifndef ROWLEVEL_H ++#define ROWLEVEL_H ++ ++#include "access/htup.h" ++#include "executor/tuptable.h" ++#include "nodes/plannodes.h" ++#include "utils/relcache.h" ++ ++#define ROWLV_BYPASS_MODE 1 ++#define ROWLV_FILTER_MODE 2 ++#define ROWLV_ABORT_MODE 3 ++ ++extern int ++rowlvGetPerformingMode(void); ++ ++extern int ++rowlvSetPerformingMode(int mode); ++ ++extern uint32 ++rowlvSetupPermissions(RangeTblEntry *rte); ++ ++extern bool ++rowlvExecScanFilter(Scan *scan, Relation rel, TupleTableSlot *slot); ++ ++extern void ++rowlvExecScanAbort(Scan *scan, Relation rel, TupleTableSlot *slot); ++ ++extern void ++rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal); ++ ++extern void ++rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup); ++ ++extern bool ++rowlvCopyToTuple(Relation rel, HeapTuple tuple); ++ ++#endif /* ROWLEVEL_H */ +diff --git a/src/include/security/sepgsql.h b/src/include/security/sepgsql.h +new file mode 100644 +index 0000000..d5ac80b +--- /dev/null ++++ b/src/include/security/sepgsql.h +@@ -0,0 +1,725 @@ ++/* ++ * src/include/security/sepgsql.h ++ * Headers of SE-PostgreSQL ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#ifndef SEPGSQL_H ++#define SEPGSQL_H ++ ++#include "access/htup.h" ++#include "catalog/dependency.h" ++#include "executor/execdesc.h" ++#include "fmgr.h" ++#include "nodes/parsenodes.h" ++#include "storage/large_object.h" ++#include "utils/relcache.h" ++ ++#ifdef HAVE_SELINUX ++ ++#include ++ ++/* GUC parameter to turn on/off SE-PostgreSQL */ ++extern int sepostgresql_mode; ++ ++#define SEPGSQL_MODE_DEFAULT 1 ++#define SEPGSQL_MODE_ENFORCING 2 ++#define SEPGSQL_MODE_PERMISSIVE 3 ++#define SEPGSQL_MODE_INTERNAL 4 ++#define SEPGSQL_MODE_DISABLED 5 ++ ++/* GUC parameter to turn on/off Row-level controls */ ++extern bool sepostgresql_row_level; ++ ++/* GUC parameter to turn on/off mcstrans */ ++extern bool sepostgresql_mcstrans; ++ ++/* Objject classes and permissions internally used */ ++enum SepgsqlClasses ++{ ++ SEPG_CLASS_PROCESS = 0, ++ SEPG_CLASS_FILE, ++ SEPG_CLASS_DIR, ++ SEPG_CLASS_LNK_FILE, ++ SEPG_CLASS_CHR_FILE, ++ SEPG_CLASS_BLK_FILE, ++ SEPG_CLASS_SOCK_FILE, ++ SEPG_CLASS_FIFO_FILE, ++ SEPG_CLASS_DB_DATABASE, ++ SEPG_CLASS_DB_SCHEMA, ++ SEPG_CLASS_DB_TABLE, ++ SEPG_CLASS_DB_VIEW, ++ SEPG_CLASS_DB_SEQUENCE, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_CLASS_DB_COLUMN, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_CLASS_MAX, ++}; ++ ++#define SEPG_PROCESS__TRANSITION (1<<0) ++ ++#define SEPG_FILE__READ (1<<0) ++#define SEPG_FILE__WRITE (1<<1) ++#define SEPG_FILE__CREATE (1<<2) ++#define SEPG_FILE__GETATTR (1<<3) ++ ++#define SEPG_DIR__READ (SEPG_FILE__READ) ++#define SEPG_DIR__WRITE (SEPG_FILE__WRITE) ++#define SEPG_DIR__CREATE (SEPG_FILE__CREATE) ++#define SEPG_DIR__GETATTR (SEPG_FILE__GETATTR) ++ ++#define SEPG_LNK_FILE__READ (SEPG_FILE__READ) ++#define SEPG_LNK_FILE__WRITE (SEPG_FILE__WRITE) ++#define SEPG_LNK_FILE__CREATE (SEPG_FILE__CREATE) ++#define SEPG_LNK_FILE__GETATTR (SEPG_FILE__GETATTR) ++ ++#define SEPG_CHR_FILE__READ (SEPG_FILE__READ) ++#define SEPG_CHR_FILE__WRITE (SEPG_FILE__WRITE) ++#define SEPG_CHR_FILE__CREATE (SEPG_FILE__CREATE) ++#define SEPG_CHR_FILE__GETATTR (SEPG_FILE__GETATTR) ++ ++#define SEPG_BLK_FILE__READ (SEPG_FILE__READ) ++#define SEPG_BLK_FILE__WRITE (SEPG_FILE__WRITE) ++#define SEPG_BLK_FILE__CREATE (SEPG_FILE__CREATE) ++#define SEPG_BLK_FILE__GETATTR (SEPG_FILE__GETATTR) ++ ++#define SEPG_SOCK_FILE__READ (SEPG_FILE__READ) ++#define SEPG_SOCK_FILE__WRITE (SEPG_FILE__WRITE) ++#define SEPG_SOCK_FILE__CREATE (SEPG_FILE__CREATE) ++#define SEPG_SOCK_FILE__GETATTR (SEPG_FILE__GETATTR) ++ ++#define SEPG_FIFO_FILE__READ (SEPG_FILE__READ) ++#define SEPG_FIFO_FILE__WRITE (SEPG_FILE__WRITE) ++#define SEPG_FIFO_FILE__CREATE (SEPG_FILE__CREATE) ++#define SEPG_FIFO_FILE__GETATTR (SEPG_FILE__GETATTR) ++ ++#define SEPG_DB_DATABASE__CREATE (1<<0) ++#define SEPG_DB_DATABASE__DROP (1<<1) ++#define SEPG_DB_DATABASE__GETATTR (1<<2) ++#define SEPG_DB_DATABASE__SETATTR (1<<3) ++#define SEPG_DB_DATABASE__RELABELFROM (1<<4) ++#define SEPG_DB_DATABASE__RELABELTO (1<<5) ++#define SEPG_DB_DATABASE__ACCESS (1<<6) ++#define SEPG_DB_DATABASE__LOAD_MODULE (1<<7) ++ ++#define SEPG_DB_SCHEMA__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_SCHEMA__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_SCHEMA__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_SCHEMA__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_SCHEMA__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_SCHEMA__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_SCHEMA__SEARCH (1<<6) ++#define SEPG_DB_SCHEMA__ADD_NAME (1<<7) ++#define SEPG_DB_SCHEMA__REMOVE_NAME (1<<8) ++ ++#define SEPG_DB_TABLE__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_TABLE__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_TABLE__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_TABLE__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_TABLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_TABLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_TABLE__SELECT (1<<6) ++#define SEPG_DB_TABLE__UPDATE (1<<7) ++#define SEPG_DB_TABLE__INSERT (1<<8) ++#define SEPG_DB_TABLE__DELETE (1<<9) ++#define SEPG_DB_TABLE__LOCK (1<<10) ++#define SEPG_DB_TABLE__REFERENCE (1<<11) ++ ++#define SEPG_DB_SEQUENCE__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_SEQUENCE__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_SEQUENCE__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_SEQUENCE__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_SEQUENCE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_SEQUENCE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_SEQUENCE__GET_VALUE (1<<6) ++#define SEPG_DB_SEQUENCE__NEXT_VALUE (1<<7) ++#define SEPG_DB_SEQUENCE__SET_VALUE (1<<8) ++ ++#define SEPG_DB_VIEW__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_VIEW__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_VIEW__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_VIEW__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_VIEW__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_VIEW__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_VIEW__USAGE (1<<6) ++ ++#define SEPG_DB_PROCEDURE__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_PROCEDURE__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_PROCEDURE__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_PROCEDURE__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_PROCEDURE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_PROCEDURE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_PROCEDURE__EXECUTE (1<<6) ++#define SEPG_DB_PROCEDURE__ENTRYPOINT (1<<7) ++#define SEPG_DB_PROCEDURE__INSTALL (1<<8) ++ ++#define SEPG_DB_COLUMN__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_COLUMN__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_COLUMN__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_COLUMN__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_COLUMN__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_COLUMN__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_COLUMN__SELECT (1<<6) ++#define SEPG_DB_COLUMN__UPDATE (1<<7) ++#define SEPG_DB_COLUMN__INSERT (1<<8) ++#define SEPG_DB_COLUMN__REFERENCE (1<<9) ++ ++#define SEPG_DB_TUPLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_TUPLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_TUPLE__SELECT (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_TUPLE__UPDATE (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_TUPLE__INSERT (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_TUPLE__DELETE (SEPG_DB_DATABASE__DROP) ++ ++#define SEPG_DB_BLOB__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_BLOB__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_BLOB__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_BLOB__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_BLOB__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_BLOB__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_BLOB__READ (1<<6) ++#define SEPG_DB_BLOB__WRITE (1<<7) ++#define SEPG_DB_BLOB__IMPORT (1<<8) ++#define SEPG_DB_BLOB__EXPORT (1<<9) ++ ++/* ++ * sepgsql_sid_t : alternative representation of security context ++ */ ++typedef struct { ++ Oid relid; ++ Oid secid; ++} sepgsql_sid_t; ++ ++#define SidIsValid(sid) (OidIsValid((sid).relid) && OidIsValid((sid).secid)) ++ ++/* ++ * selinux.c : communication to in-kernel SELinux ++ */ ++extern void sepgsqlInitialize(void); ++extern Size sepgsqlShmemSize(void); ++extern bool sepgsqlIsEnabled(void); ++extern bool sepgsqlIsEnabledBootstrap(void); ++extern bool sepgsqlGetEnforce(void); ++extern char *sepgsqlShowMode(void); ++extern char *sepgsqlGetServerLabel(void); ++extern char *sepgsqlGetClientLabel(void); ++extern char *sepgsqlSetClientLabel(char *new_label); ++extern bool ++sepgsqlComputePerms(char *scontext, char *tcontext, ++ uint16 tclass, uint32 required, ++ const char *audit_name, bool abort); ++extern char * ++sepgsqlComputeCreate(char *scontext, char *tcontext, uint16 tclass); ++extern bool ++sepgsqlClientHasPerms(sepgsql_sid_t tsid, uint16 tclass, uint32 required, ++ const char *audit_name, bool abort); ++extern sepgsql_sid_t ++sepgsqlClientCreateSecid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid); ++extern char * ++sepgsqlClientCreateLabel(sepgsql_sid_t tsid, uint16 tclass); ++ ++extern bool sepgsqlReceiverStart(void); ++extern void sepgsqlReceiverMain(void); ++ ++/* ++ * bridge.c : new style security hooks ++ */ ++ ++/* pg_attribute */ ++extern Oid ++sepgsql_attribute_create(Oid relOid, ColumnDef *cdef); ++extern void ++sepgsql_attribute_alter(Oid relOid, const char *attname); ++extern void ++sepgsql_attribute_drop(Oid relOid, AttrNumber attnum); ++extern void ++sepgsql_attribute_grant(Oid relOid, AttrNumber attnum); ++extern Oid ++sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, DefElem *newLabel); ++ ++/* pg_cast */ ++extern Oid ++sepgsql_cast_create(Oid sourceTypOid, Oid targetTypOid, Oid funcOid); ++extern void ++sepgsql_cast_drop(Oid castOid); ++ ++/* pg_class */ ++extern Oid * ++sepgsql_relation_create(const char *relName, ++ char relkind, ++ TupleDesc tupDesc, ++ Oid nspOid, ++ DefElem *relLabel, ++ List *colList, ++ bool createAs, ++ bool permission); ++extern Oid * ++sepgsql_relation_copy(Relation src); ++extern void ++sepgsql_relation_alter(Oid relOid, const char *newName, Oid newNsp); ++extern void ++sepgsql_relation_drop(Oid relOid); ++extern void ++sepgsql_relation_grant(Oid relOid); ++extern Oid ++sepgsql_relation_relabel(Oid relOid, DefElem *newLabel); ++extern void ++sepgsql_relation_get_transaction_id(Oid relOid); ++extern void ++sepgsql_relation_copy_definition(Oid relOid); ++extern void ++sepgsql_relation_truncate(Relation rel); ++extern void ++sepgsql_relation_references(Relation rel, int16 *attnums, int natts); ++extern void ++sepgsql_relation_lock(Oid relOid); ++extern void ++sepgsql_view_replace(Oid viewOid); ++extern void ++sepgsql_index_create(Oid relOid, Oid nspOid); ++extern void ++sepgsql_sequence_get_value(Oid seqOid); ++extern void ++sepgsql_sequence_next_value(Oid seqOid); ++extern void ++sepgsql_sequence_set_value(Oid seqOid); ++ ++/* pg_conversion */ ++extern Oid ++sepgsql_conversion_create(const char *convName, Oid nspOid, Oid procOid); ++extern void ++sepgsql_conversion_alter(Oid convOid, const char *newName); ++extern void ++sepgsql_conversion_drop(Oid convOid); ++ ++/* pg_database */ ++extern Oid ++sepgsql_database_create(const char *datName, Oid srcDatOid, DefElem *newLabel); ++extern void ++sepgsql_database_alter(Oid datOid); ++extern void ++sepgsql_database_drop(Oid datOid); ++extern Oid ++sepgsql_database_relabel(Oid datOid, DefElem *newLabel); ++extern void ++sepgsql_database_grant(Oid datOid); ++extern void ++sepgsql_database_access(Oid datOid); ++extern bool ++sepgsql_database_superuser(Oid datOid); ++extern void ++sepgsql_database_load_module(Oid datOid, const char *filename); ++ ++/* pg_foreign_data_wrapper */ ++extern Oid ++sepgsql_fdw_create(const char *fdwName, Oid fdwValidator); ++extern void ++sepgsql_fdw_alter(Oid fdwOid, Oid newValidator); ++extern void ++sepgsql_fdw_drop(Oid fdwOid); ++extern void ++sepgsql_fdw_grant(Oid fdwOid); ++ ++/* pg_foreign_server */ ++extern Oid ++sepgsql_foreign_server_create(const char *fsrvName); ++extern void ++sepgsql_foreign_server_alter(Oid fsrvOid); ++extern void ++sepgsql_foreign_server_drop(Oid fsrvOid); ++extern void ++sepgsql_foreign_server_grant(Oid fsrvOid); ++ ++/* pg_language */ ++extern Oid ++sepgsql_language_create(const char *langName, Oid handlerOid, Oid validatorOid); ++extern void ++sepgsql_language_alter(Oid langOid); ++extern void ++sepgsql_language_drop(Oid langOid); ++extern void ++sepgsql_language_grant(Oid langOid); ++ ++/* pg_largeobject */ ++extern Oid ++sepgsql_largeobject_create(Oid loid, Value *secLabel); ++extern void ++sepgsql_largeobject_alter(Oid loid); ++extern void ++sepgsql_largeobject_relabel(Oid loid, Value *secLabel); ++extern void ++sepgsql_largeobject_drop(Oid loid); ++extern void ++sepgsql_largeobject_read(Oid loid, Snapshot snapshot); ++extern void ++sepgsql_largeobject_write(Oid loid, Snapshot snapshot); ++extern void ++sepgsql_largeobject_export(Oid loid, const char *filename); ++extern Oid ++sepgsql_largeobject_import(Oid loid, const char *filename); ++ ++/* pg_namespace */ ++extern Oid ++sepgsql_schema_create(const char *nspName, bool isTemp, DefElem *newLabel); ++extern void ++sepgsql_schema_alter(Oid nspOid); ++extern void ++sepgsql_schema_drop(Oid nspOid); ++extern Oid ++sepgsql_schema_relabel(Oid nspOid, DefElem *newLabel); ++extern void ++sepgsql_schema_grant(Oid nspOid); ++extern bool ++sepgsql_schema_search(Oid nspOid, bool abort); ++ ++/* pg_opclass */ ++extern Oid ++sepgsql_opclass_create(const char *opcName, Oid nspOid); ++extern void ++sepgsql_opclass_alter(Oid opcOid, const char *newName); ++extern void ++sepgsql_opclass_drop(Oid opcOid); ++ ++/* pg_opfamily */ ++extern Oid ++sepgsql_opfamily_create(const char *opfName, Oid nspOid); ++extern void ++sepgsql_opfamily_alter(Oid opfOid, const char *newName); ++extern void ++sepgsql_opfamily_drop(Oid opfOid); ++extern void ++sepgsql_opfamily_add_operator(Oid opfOid, Oid operOid); ++extern void ++sepgsql_opfamily_add_procedure(Oid opfOid, Oid procOid); ++ ++/* pg_operator */ ++extern Oid ++sepgsql_operator_create(const char *oprName, Oid oprOid, Oid nspOid, ++ Oid codeFn, Oid restFn, Oid joinFn); ++extern void ++sepgsql_operator_alter(Oid oprOid); ++extern void ++sepgsql_operator_drop(Oid oprOid); ++ ++/* pg_proc */ ++extern Oid ++sepgsql_proc_create(const char *procName, HeapTuple oldTup, ++ Oid nspOid, Oid langOid, DefElem *newLabel); ++extern void ++sepgsql_proc_alter(Oid procOid, const char *newName, Oid newNsp); ++extern void ++sepgsql_proc_drop(Oid procOid); ++extern Oid ++sepgsql_proc_relabel(Oid procOid, DefElem *newLabel); ++extern void ++sepgsql_proc_grant(Oid procOid); ++extern void ++sepgsql_proc_execute(Oid procOid); ++extern bool ++sepgsql_proc_hint_inlined(HeapTuple protup); ++extern bool ++sepgsql_proc_entrypoint(HeapTuple protup); ++extern char * ++sepgsql_proc_trusted(HeapTuple protup, MemoryContext mcxt); ++ ++/* pg_rewrite */ ++extern void ++sepgsql_rule_create(Oid relOid, const char *ruleName); ++extern void ++sepgsql_rule_drop(Oid relOid, const char *ruleName); ++ ++/* pg_trigger */ ++extern void ++sepgsql_trigger_create(Oid relOid, const char *trigName, Oid procOid); ++extern void ++sepgsql_trigger_alter(Oid relOid, const char *trigName); ++extern void ++sepgsql_trigger_drop(Oid relOid, const char *trigName); ++ ++/* pg_ts_config */ ++extern Oid ++sepgsql_ts_config_create(const char *cfgName, Oid nspOid); ++extern void ++sepgsql_ts_config_alter(Oid cfgOid, const char *newName); ++extern void ++sepgsql_ts_config_drop(Oid cfgOid); ++ ++/* pg_ts_dict */ ++extern Oid ++sepgsql_ts_dict_create(const char *dictName, Oid nspOid); ++extern void ++sepgsql_ts_dict_alter(Oid dictOid, const char *newName); ++extern void ++sepgsql_ts_dict_drop(Oid dictOid); ++ ++/* pg_ts_parser */ ++extern Oid ++sepgsql_ts_parser_create(const char *prsName, Oid nspOid, ++ Oid startFn, Oid tokenFn, Oid sendFn, ++ Oid headlineFn, Oid lextypeFn); ++extern void ++sepgsql_ts_parser_alter(Oid prsOid, const char *newName); ++extern void ++sepgsql_ts_parser_drop(Oid prsOid); ++ ++/* pg_ts_templace */ ++extern Oid ++sepgsql_ts_template_create(const char *tmplName, Oid nspOid, ++ Oid initFn, Oid lexizeFn); ++extern void ++sepgsql_ts_template_alter(Oid tmplOid, const char *newName); ++extern void ++sepgsql_ts_template_drop(Oid tmplOid); ++ ++/* pg_type */ ++extern Oid ++sepgsql_type_create(const char *typName, HeapTuple oldTup, Oid nspOid, ++ Oid inputProc, Oid outputProc, Oid recvProc, Oid sendProc, ++ Oid modinProc, Oid modoutProc, Oid analyzeProc); ++extern void ++sepgsql_type_alter(Oid typOid, const char *newName, Oid newNsp); ++extern void ++sepgsql_type_drop(Oid typOid); ++ ++/* misc objects */ ++extern void ++sepgsql_sysobj_drop(const ObjectAddress *object); ++ ++/* filesystem objects */ ++void ++sepgsql_file_stat(const char *filename); ++void ++sepgsql_file_read(const char *filename); ++void ++sepgsql_file_write(const char *filename); ++ ++/* ++ * checker.c : check permission on given queries ++ */ ++extern void ++sepgsqlCheckRTEPerms(RangeTblEntry *rte); ++ ++extern void ++sepgsqlCheckCopyTable(Relation rel, List *attnumlist, bool is_from); ++ ++extern void ++sepgsqlCheckSelectInto(Oid relaionId); ++ ++extern bool ++sepgsqlExecScan(Relation rel, HeapTuple tuple, uint32 required, bool abort); ++ ++extern uint32 ++sepgsqlSetupTuplePerms(RangeTblEntry *rte); ++ ++extern void ++sepgsqlHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal); ++ ++extern void ++sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup); ++ ++/* ++ * label.c : security label management ++ */ ++extern bool sepgsqlTupleDescHasSecid(Oid relid, char relkind); ++ ++extern void sepgsqlPostBootstrapingMode(void); ++ ++extern void sepgsqlSetDefaultSecid(Relation rel, HeapTuple tuple); ++extern sepgsql_sid_t sepgsqlGetDefaultDatabaseSecid(Oid src_database_oid); ++extern sepgsql_sid_t sepgsqlGetDefaultSchemaSecid(Oid database_oid); ++extern sepgsql_sid_t sepgsqlGetDefaultSchemaTempSecid(Oid database_oid); ++extern sepgsql_sid_t sepgsqlGetDefaultTableSecid(Oid namespace_oid); ++extern sepgsql_sid_t sepgsqlGetDefaultSequenceSecid(Oid namespace_oid); ++extern sepgsql_sid_t sepgsqlGetDefaultProcedureSecid(Oid namespace_oid); ++extern sepgsql_sid_t sepgsqlGetDefaultColumnSecid(Oid table_oid); ++extern sepgsql_sid_t sepgsqlGetDefaultTupleSecid(Oid table_oid); ++extern sepgsql_sid_t sepgsqlGetDefaultBlobSecid(Oid database_oid); ++ ++extern Oid *sepgsqlCreateTableColumns(CreateStmt *stmt, ++ const char *relname, Oid namespace_oid, ++ TupleDesc tupdesc, char relkind); ++extern Oid *sepgsqlCopyTableColumns(Relation source); ++ ++extern sepgsql_sid_t ++sepgsqlGetTupleSecid(Oid tableOid, HeapTuple tuple, uint16 *tclass); ++extern sepgsql_sid_t ++sepgsqlGetSysobjSecid(Oid tableOid, Oid objectId, int32 objsubId, uint16 *tclass); ++ ++extern char *sepgsqlTransSecLabelIn(char *seclabel); ++extern char *sepgsqlTransSecLabelOut(char *seclabel); ++extern char *sepgsqlRawSecLabelIn(char *seclabel); ++extern char *sepgsqlRawSecLabelOut(char *seclabel); ++extern char *sepgsqlSysattSecLabelOut(Oid relid, HeapTuple tuple); ++ ++#else /* HAVE_SELINUX */ ++ ++/* avc.c */ ++#define sepgsqlShmemSize() (0) ++ ++/* checker.c */ ++#define sepgsqlCheckRTEPerms(a) do {} while(0) ++#define sepgsqlCheckCopyTable(a,b,c) do {} while(0) ++#define sepgsqlCheckSelectInto(a) do {} while(0) ++#define sepgsqlExecScan(a,b,c) (true) ++#define sepgsqlSetupTuplePerms(a) (0) ++#define sepgsqlHeapTupleInsert(a,b,c) do {} while(0) ++#define sepgsqlHeapTupleUpdate(a,b,c) do {} while(0) ++ ++/* core.c */ ++#define sepgsqlIsEnabled() (false) ++#define sepgsqlInitialize() do {} while(0) ++ ++/* bridge.c */ ++#define sepgsql_attribute_create(a,b) (InvalidOid) ++#define sepgsql_attribute_alter(a,b) do {} while(0) ++#define sepgsql_attribute_drop(a,b) do {} while(0) ++#define sepgsql_attribute_grant(a,b) do {} while(0) ++#define sepgsql_attribute_relabel(a,b,c) (InvalidOid) ++ ++#define sepgsql_cast_create(a,b,c) (InvalidOid) ++#define sepgsql_cast_drop(a) (InvalidOid) ++ ++#define sepgsql_relation_create(a,b,c,d,e,f) (NULL) ++#define sepgsql_relation_copy(a) (NULL) ++#define sepgsql_relation_alter(a,b,c) do {} while(0) ++#define sepgsql_relation_drop(a) do {} while(0) ++#define sepgsql_relation_grant(a) do {} while(0) ++#define sepgsql_relation_relabel(a,b) do {} while(0) ++#define sepgsql_relation_get_transaction_id(a) do {} while(0) ++#define sepgsql_relation_copy_definition(a) do {} while(0) ++#define sepgsql_relation_truncate(a) do {} while(0) ++#define sepgsql_relation_references(a,b,c) do {} while(0) ++#define sepgsql_relation_lock(a) do {} while(0) ++#define sepgsql_view_replace(a) do {} while(0) ++#define sepgsql_index_create(a,b,c) do {} while(0) ++#define sepgsql_sequence_get_value(a) do {} while(0) ++#define sepgsql_sequence_next_value(a) do {} while(0) ++#define sepgsql_sequence_set_value(a) do {} while(0) ++ ++#define sepgsql_conversion_create(a,b,c) do {} while(0) ++#define sepgsql_conversion_alter(a,b) do {} while(0) ++#define sepgsql_conversion_drop(a) do {} while(0) ++ ++#define sepgsql_database_create(a,b) (InvalidOid) ++#define sepgsql_database_alter(a) do {} while(0) ++#define sepgsql_database_drop(a) do {} while(0) ++#define sepgsql_database_relabel(a,b) (InvalidOid) ++#define sepgsql_database_grant(a) do {} while(0) ++#define sepgsql_database_access(a) do {} while(0) ++#define sepgsql_database_superuser(a) (true) ++#define sepgsql_database_load_module(a,b) do {} while(0) ++ ++#define sepgsql_fdw_create(a,b) (InvalidOid) ++#define sepgsql_fdw_alter(a,b) do {} while(0) ++#define sepgsql_fdw_drop(a) do {} while(0) ++#define sepgsql_fdw_grant(a) do {} while(0) ++ ++#define sepgsql_foreign_server_create(a) (InvalidOid) ++#define sepgsql_foreign_server_alter(a) do {} while(0) ++#define sepgsql_foreign_server_drop(a) do {} while(0) ++#define sepgsql_foreign_server_grant(a) do {} while(0) ++ ++#define sepgsql_language_create(a,b,c) (InvalidOid) ++#define sepgsql_language_alter(a) do {} while(0) ++#define sepgsql_language_drop(a) do {} while(0) ++#define sepgsql_language_grant(a) do {} while(0) ++ ++#define sepgsql_largeobject_create(a,b) (InvalidOid) ++#define sepgsql_largeobject_alter(a,b) do {} while(0) ++#define sepgsql_largeobject_drop(a) do {} while(0) ++#define sepgsql_largeobject_read(a) do {} while(0) ++#define sepgsql_largeobject_write(a) do {} while(0) ++#define sepgsql_largeobject_export(a,b) do {} while(0) ++#define sepgsql_largeobject_import(a,b) (InvalidOid) ++ ++#define sepgsql_schema_create(a,b,c) (InvalidOid) ++#define sepgsql_schema_alter(a) do {} while(0) ++#define sepgsql_schema_drop(a) do {} while(0) ++#define sepgsql_schema_relabel(a,b) (InvalidOid) ++#define sepgsql_schema_grant(a) do {} while(0) ++#define sepgsql_schema_search(a,b) (true) ++ ++#define sepgsql_opclass_create(a,b) (InvalidOid) ++#define sepgsql_opclass_alter(a,b) do {} while(0) ++#define sepgsql_opclass_drop(a) do {} while(0) ++ ++#define sepgsql_opfamily_create(a,b) (InvalidOid) ++#define sepgsql_opfamily_alter(a,b) do {} while(0) ++#define sepgsql_opfamily_drop(a) do {} while(0) ++#define sepgsql_opfamily_add_operator(a,b) do {} while(0) ++#define sepgsql_opfamily_add_procedure(a,b) do {} while(0) ++ ++#define sepgsql_operator_create(a,b,c,d,e,f) (InvalidOid) ++#define sepgsql_operator_alter(a) do {} while(0) ++#define sepgsql_operator_drop(a) do {} while(0) ++ ++#define sepgsql_proc_create(a,b,c,d,e) (InvalidOid) ++#define sepgsql_proc_alter(a,b,c) do {} while(0) ++#define sepgsql_proc_drop(a) do {} while(0) ++#define sepgsql_proc_relabel(a,b) (InvalidOid) ++#define sepgsql_proc_grant(a) do {} while(0) ++#define sepgsql_proc_execute(a) do {} while(0) ++#define sepgsql_proc_hint_inlined(a) (true) ++#define sepgsql_proc_entrypoint(a,b) do {} while(0) ++ ++#define sepgsql_rule_create(a,b) do {} while(0) ++#define sepgsql_rule_drop(a,b) do {} while(0) ++ ++#define sepgsql_trigger_create(a,b,c) do {} while(0) ++#define sepgsql_trigger_alter(a,b) do {} while(0) ++#define sepgsql_trigger_drop(a,b) do {} while(0) ++ ++#define sepgsql_ts_config_create(a,b) (InvalidOid) ++#define sepgsql_ts_config_alter(a,b) do {} while(0) ++#define sepgsql_ts_config_drop(a) do {} while(0) ++ ++#define sepgsql_ts_config_create(a,b) (InvalidOid) ++#define sepgsql_ts_config_alter(a,b) do {} while(0) ++#define sepgsql_ts_config_drop(a) do {} while(0) ++ ++#define sepgsql_ts_dict_create(a,b) (InvalidOid) ++#define sepgsql_ts_dict_alter(a,b) do {} while(0) ++#define sepgsql_ts_dict_drop(a) do {} while(0) ++ ++#define sepgsql_ts_parser_create(a,b,c,d,e,f,g) (InvalidOid) ++#define sepgsql_ts_parser_alter(a,b) do {} while(0) ++#define sepgsql_ts_parser_drop(a) do {} while(0) ++ ++#define sepgsql_ts_template_create(a,b,c,d) (InvalidOid) ++#define sepgsql_ts_template_alter(a,b) do {} while(0) ++#define sepgsql_ts_template_drop(a) do {} while(0) ++ ++#define sepgsql_type_create(a,b,c,d,e,f,g,h,i,j) (InvalidOid) ++#define sepgsql_type_alter(a,b,c) do {} while(0) ++#define sepgsql_type_drop(a) do {} while(0) ++ ++#define sepgsql_sysobj_drop(a) do {} while(0) ++ ++#define sepgsql_file_stat(a) do {} while(0) ++#define sepgsql_file_read(a) do {} while(0) ++#define sepgsql_file_write(a) do {} while(0) ++ ++/* label.c */ ++#define sepgsqlTupleDescHasSecLabel(a,b) (false) ++#define sepgsqlSetDefaultSecLabel(a,b) do {} while(0) ++#define sepgsqlTransSecLabelIn(a) (a) ++#define sepgsqlTransSecLabelOut(a) (a) ++#define sepgsqlRawSecLabelIn(a) (a) ++#define sepgsqlRawSecLabelOut(a) (a) ++ ++#endif /* HAVE_SELINUX */ ++ ++extern Datum sepgsql_getcon(PG_FUNCTION_ARGS); ++extern Datum sepgsql_server_getcon(PG_FUNCTION_ARGS); ++extern Datum sepgsql_get_user(PG_FUNCTION_ARGS); ++extern Datum sepgsql_get_role(PG_FUNCTION_ARGS); ++extern Datum sepgsql_get_type(PG_FUNCTION_ARGS); ++extern Datum sepgsql_get_range(PG_FUNCTION_ARGS); ++extern Datum sepgsql_set_user(PG_FUNCTION_ARGS); ++extern Datum sepgsql_set_role(PG_FUNCTION_ARGS); ++extern Datum sepgsql_set_type(PG_FUNCTION_ARGS); ++extern Datum sepgsql_set_range(PG_FUNCTION_ARGS); ++ ++#endif /* SEPGSQL_H */ +diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h +index 462f6d1..d2c5e6e 100644 +--- a/src/include/storage/fd.h ++++ b/src/include/storage/fd.h +@@ -68,6 +68,7 @@ extern int FileWrite(File file, char *buffer, int amount); + extern int FileSync(File file); + extern off_t FileSeek(File file, off_t offset, int whence); + extern int FileTruncate(File file, off_t offset); ++extern int FileRawDescriptor(File file); + + /* Operations that allow use of regular stdio --- USE WITH CAUTION */ + extern FILE *AllocateFile(const char *name, const char *mode); +diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h +index 818db40..feb066f 100644 +--- a/src/include/storage/large_object.h ++++ b/src/include/storage/large_object.h +@@ -70,7 +70,7 @@ typedef struct LargeObjectDesc + + /* inversion stuff in inv_api.c */ + extern void close_lo_relation(bool isCommit); +-extern Oid inv_create(Oid lobjId); ++extern Oid inv_create(Oid lobjId, Oid secid); + extern LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt); + extern void inv_close(LargeObjectDesc *obj_desc); + extern int inv_drop(Oid lobjId); +diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h +index e389c61..1ea6c09 100644 +--- a/src/include/storage/lwlock.h ++++ b/src/include/storage/lwlock.h +@@ -67,6 +67,7 @@ typedef enum LWLockId + AutovacuumLock, + AutovacuumScheduleLock, + SyncScanLock, ++ SepgsqlAvcLock, + /* Individual lock IDs end here */ + FirstBufMappingLock, + FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, +diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h +index 48872d9..3383637 100644 +--- a/src/include/storage/proc.h ++++ b/src/include/storage/proc.h +@@ -143,8 +143,10 @@ typedef struct PROC_HDR + * normal operation. Startup process also consumes one slot, but WAL + * writer and autovacuum launcher are launched only after it has + * exited. ++ * In addition, a netlink receiver process may be launched, if SELinux ++ * support is enabled. + */ +-#define NUM_AUXILIARY_PROCS 3 ++#define NUM_AUXILIARY_PROCS 4 + + + /* configurable options */ +diff --git a/src/include/utils/errcodes.h b/src/include/utils/errcodes.h +index 44018cd..b225468 100644 +--- a/src/include/utils/errcodes.h ++++ b/src/include/utils/errcodes.h +@@ -301,6 +301,7 @@ + #define ERRCODE_INVALID_SCHEMA_DEFINITION MAKE_SQLSTATE('4','2', 'P','1','5') + #define ERRCODE_INVALID_TABLE_DEFINITION MAKE_SQLSTATE('4','2', 'P','1','6') + #define ERRCODE_INVALID_OBJECT_DEFINITION MAKE_SQLSTATE('4','2', 'P','1','7') ++#define ERRCODE_INVALID_SECURITY_LABEL MAKE_SQLSTATE('4','2', 'P','9','9') + + /* Class 44 - WITH CHECK OPTION Violation */ + #define ERRCODE_WITH_CHECK_OPTION_VIOLATION MAKE_SQLSTATE('4','4', '0','0','0') +diff --git a/src/test/regress/GNUmakefile b/src/test/regress/GNUmakefile +index 8ce5d25..70d7def 100644 +--- a/src/test/regress/GNUmakefile ++++ b/src/test/regress/GNUmakefile +@@ -38,6 +38,12 @@ ifdef NO_LOCALE + NOLOCALE += --no-locale + endif + ++# SELinux support ++ENABLE_SELINUX = ++ifdef SELINUX ++ENABLE_SELINUX += --enable-selinux ++endif ++ + # stuff to pass into build of pg_regress + EXTRADEFS = '-DHOST_TUPLE="$(host_tuple)"' \ + '-DMAKEPROG="$(MAKE)"' \ +@@ -138,7 +144,7 @@ tablespace-setup: + ## Run tests + ## + +-pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) ++pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) $(ENABLE_SELINUX) + + check: all + $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) +diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out +index 9a66ba0..e0efefc 100644 +--- a/src/test/regress/expected/sanity_check.out ++++ b/src/test/regress/expected/sanity_check.out +@@ -113,6 +113,7 @@ SELECT relname, relhasindex + pg_pltemplate | t + pg_proc | t + pg_rewrite | t ++ pg_security | t + pg_shdepend | t + pg_shdescription | t + pg_statistic | t +diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c +index 37193ae..a5f05cc 100644 +--- a/src/test/regress/pg_regress.c ++++ b/src/test/regress/pg_regress.c +@@ -82,6 +82,7 @@ static _stringlist *schedulelist = NULL; + static _stringlist *extra_tests = NULL; + static char *temp_install = NULL; + static char *temp_config = NULL; ++static bool enable_selinux = false; + static char *top_builddir = NULL; + static bool nolocale = false; + static char *hostname = NULL; +@@ -1863,6 +1864,7 @@ help(void) + printf(_(" --top-builddir=DIR (relative) path to top level build directory\n")); + printf(_(" --port=PORT start postmaster on PORT\n")); + printf(_(" --temp-config=PATH append contents of PATH to temporary config\n")); ++ printf(_(" --enable-selinux enables SELinux support, if available\n")); + printf(_("\n")); + printf(_("Options for using an existing installation:\n")); + printf(_(" --host=HOST use postmaster running on HOST\n")); +@@ -1907,6 +1909,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc + {"dlpath", required_argument, NULL, 17}, + {"create-role", required_argument, NULL, 18}, + {"temp-config", required_argument, NULL, 19}, ++ {"enable-selinux", optional_argument, NULL, 20}, + {NULL, 0, NULL, 0} + }; + +@@ -1997,6 +2000,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc + case 19: + temp_config = strdup(optarg); + break; ++ case 20: ++ enable_selinux = true; ++ break; + default: + /* getopt_long already emitted a complaint */ + fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"), +@@ -2086,10 +2092,11 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc + /* initdb */ + header(_("initializing database system")); + snprintf(buf, sizeof(buf), +- SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE, ++ SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE, + bindir, temp_install, datadir, + debug ? " --debug" : "", + nolocale ? " --no-locale" : "", ++ enable_selinux ? " --enable-selinux" : "", + outputdir); + if (system(buf)) + { diff --git a/sepostgresql-8.4.patch b/sepostgresql-8.4.patch deleted file mode 100644 index 0a1331d..0000000 --- a/sepostgresql-8.4.patch +++ /dev/null @@ -1,19117 +0,0 @@ -diff -Nrpc base/configure sepgsql/configure -*** base/configure Tue Dec 15 17:16:51 2009 ---- sepgsql/configure Tue Dec 15 17:30:25 2009 -*************** with_libxml -*** 710,715 **** ---- 710,716 ---- - with_libxslt - with_system_tzdata - with_zlib -+ enable_selinux - GREP - EGREP - ELF_SYS -*************** Optional Features: -*** 1378,1383 **** ---- 1379,1385 ---- - --enable-thread-safety make client libraries thread-safe - --enable-thread-safety-force - force thread-safety despite thread test failure -+ --enable-selinux enable to build with SELinux support - --disable-float4-byval disable float4 passed by value - --disable-float8-byval disable float8 passed by value - --disable-largefile omit support for large files -*************** fi -*** 5532,5537 **** ---- 5534,5645 ---- - - - # -+ # SELinux support -+ # -+ -+ pgac_args="$pgac_args enable_selinux" -+ -+ # Check whether --enable-selinux was given. -+ if test "${enable_selinux+set}" = set; then -+ enableval=$enable_selinux; -+ case $enableval in -+ yes) -+ : -+ ;; -+ no) -+ : -+ ;; -+ *) -+ { { echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5 -+ echo "$as_me: error: no argument expected for --enable-selinux option" >&2;} -+ { (exit 1); exit 1; }; } -+ ;; -+ esac -+ -+ else -+ enable_selinux=no -+ -+ fi -+ -+ -+ if test "$enable_selinux" = yes; then -+ { echo "$as_me:$LINENO: checking for avc_netlink_loop in -lselinux" >&5 -+ echo $ECHO_N "checking for avc_netlink_loop in -lselinux... $ECHO_C" >&6; } -+ if test "${ac_cv_lib_selinux_avc_netlink_loop+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+ else -+ ac_check_lib_save_LIBS=$LIBS -+ LIBS="-lselinux $LIBS" -+ cat >conftest.$ac_ext <<_ACEOF -+ /* confdefs.h. */ -+ _ACEOF -+ cat confdefs.h >>conftest.$ac_ext -+ cat >>conftest.$ac_ext <<_ACEOF -+ /* end confdefs.h. */ -+ -+ /* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+ #ifdef __cplusplus -+ extern "C" -+ #endif -+ char avc_netlink_loop (); -+ int -+ main () -+ { -+ return avc_netlink_loop (); -+ ; -+ return 0; -+ } -+ _ACEOF -+ rm -f conftest.$ac_objext conftest$ac_exeext -+ if { (ac_try="$ac_link" -+ case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+ esac -+ eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_link") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest$ac_exeext && -+ $as_test_x conftest$ac_exeext; then -+ ac_cv_lib_selinux_avc_netlink_loop=yes -+ else -+ echo "$as_me: failed program was:" >&5 -+ sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_cv_lib_selinux_avc_netlink_loop=no -+ fi -+ -+ rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ -+ conftest$ac_exeext conftest.$ac_ext -+ LIBS=$ac_check_lib_save_LIBS -+ fi -+ { echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_avc_netlink_loop" >&5 -+ echo "${ECHO_T}$ac_cv_lib_selinux_avc_netlink_loop" >&6; } -+ if test $ac_cv_lib_selinux_avc_netlink_loop = yes; then -+ -+ cat >>confdefs.h <<_ACEOF -+ #define HAVE_SELINUX 1 -+ _ACEOF -+ -+ -+ else -+ { { echo "$as_me:$LINENO: error: \"--enable-selinux requires libselinux.\"" >&5 -+ echo "$as_me: error: \"--enable-selinux requires libselinux.\"" >&2;} -+ { (exit 1); exit 1; }; } -+ fi -+ -+ fi -+ -+ # - # Elf - # - -*************** with_libxml!$with_libxml$ac_delim -*** 28137,28147 **** - with_libxslt!$with_libxslt$ac_delim - with_system_tzdata!$with_system_tzdata$ac_delim - with_zlib!$with_zlib$ac_delim - GREP!$GREP$ac_delim - EGREP!$EGREP$ac_delim - ELF_SYS!$ELF_SYS$ac_delim - LDFLAGS_SL!$LDFLAGS_SL$ac_delim -- LD!$LD$ac_delim - _ACEOF - - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then ---- 28245,28255 ---- - with_libxslt!$with_libxslt$ac_delim - with_system_tzdata!$with_system_tzdata$ac_delim - with_zlib!$with_zlib$ac_delim -+ enable_selinux!$enable_selinux$ac_delim - GREP!$GREP$ac_delim - EGREP!$EGREP$ac_delim - ELF_SYS!$ELF_SYS$ac_delim - LDFLAGS_SL!$LDFLAGS_SL$ac_delim - _ACEOF - - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then -*************** _ACEOF -*** 28183,28188 **** ---- 28291,28297 ---- - ac_delim='%!_!# ' - for ac_last_try in false false false false false :; do - cat >conf$$subs.sed <<_ACEOF -+ LD!$LD$ac_delim - with_gnu_ld!$with_gnu_ld$ac_delim - ld_R_works!$ld_R_works$ac_delim - RANLIB!$RANLIB$ac_delim -*************** vpath_build!$vpath_build$ac_delim -*** 28245,28251 **** - LTLIBOBJS!$LTLIBOBJS$ac_delim - _ACEOF - -! if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 60; then - break - elif $ac_last_try; then - { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 ---- 28354,28360 ---- - LTLIBOBJS!$LTLIBOBJS$ac_delim - _ACEOF - -! if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 61; then - break - elif $ac_last_try; then - { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 -diff -Nrpc base/configure.in sepgsql/configure.in -*** base/configure.in Tue Dec 15 17:16:51 2009 ---- sepgsql/configure.in Tue Dec 15 17:30:25 2009 -*************** PGAC_ARG_BOOL(with, zlib, yes, -*** 764,769 **** ---- 764,782 ---- - AC_SUBST(with_zlib) - - # -+ # SELinux support -+ # -+ PGAC_ARG_BOOL(enable, selinux, no, -+ [enable to build with SELinux support]) -+ if test "$enable_selinux" = yes; then -+ AC_CHECK_LIB(selinux, avc_netlink_loop, -+ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, -+ [SE-PostgreSQL feature is enabled]) -+ AC_SUBST(enable_selinux), -+ AC_MSG_ERROR("--enable-selinux requires libselinux.")) -+ fi -+ -+ # - # Elf - # - -diff -Nrpc base/src/Makefile.global.in sepgsql/src/Makefile.global.in -*** base/src/Makefile.global.in Tue Jun 30 01:26:47 2009 ---- sepgsql/src/Makefile.global.in Wed Jul 15 19:35:52 2009 -*************** enable_rpath = @enable_rpath@ -*** 164,169 **** ---- 164,170 ---- - enable_nls = @enable_nls@ - enable_debug = @enable_debug@ - enable_dtrace = @enable_dtrace@ -+ enable_selinux = @enable_selinux@ - enable_coverage = @enable_coverage@ - enable_thread_safety = @enable_thread_safety@ - -diff -Nrpc base/src/backend/Makefile sepgsql/src/backend/Makefile -*** base/src/backend/Makefile Tue Dec 15 17:16:51 2009 ---- sepgsql/src/backend/Makefile Tue Dec 15 17:30:25 2009 -*************** include $(top_builddir)/src/Makefile.glo -*** 16,22 **** - - SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ - main nodes optimizer port postmaster regex rewrite \ -! storage tcop tsearch utils $(top_builddir)/src/timezone - - include $(srcdir)/common.mk - ---- 16,22 ---- - - SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ - main nodes optimizer port postmaster regex rewrite \ -! security storage tcop tsearch utils $(top_builddir)/src/timezone - - include $(srcdir)/common.mk - -*************** LIBS := $(filter-out -lpgport, $(LIBS)) -*** 40,45 **** ---- 40,50 ---- - # The backend doesn't need everything that's in LIBS, however - LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) - -+ # SELinux needs libselinux -+ ifeq ($(enable_selinux), yes) -+ LIBS += -lselinux -+ endif -+ - ########################################################################## - - all: submake-libpgport postgres $(POSTGRES_IMP) -diff -Nrpc base/src/backend/access/common/heaptuple.c sepgsql/src/backend/access/common/heaptuple.c -*** base/src/backend/access/common/heaptuple.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/access/common/heaptuple.c Thu Sep 10 15:18:03 2009 -*************** -*** 60,65 **** ---- 60,66 ---- - #include "access/heapam.h" - #include "access/sysattr.h" - #include "access/tuptoaster.h" -+ #include "catalog/pg_security.h" - #include "executor/tuptable.h" - - -*************** heap_attisnull(HeapTuple tup, int attnum -*** 287,292 **** ---- 288,294 ---- - case MinCommandIdAttributeNumber: - case MaxTransactionIdAttributeNumber: - case MaxCommandIdAttributeNumber: -+ case SecurityAttributeNumber: - /* these are never null */ - break; - -*************** heap_getsysattr(HeapTuple tup, int attnu -*** 599,604 **** ---- 601,609 ---- - case TableOidAttributeNumber: - result = ObjectIdGetDatum(tup->t_tableOid); - break; -+ case SecurityAttributeNumber: -+ result = securitySysattSecLabelOut(tup->t_tableOid, tup); -+ break; - default: - elog(ERROR, "invalid attnum: %d", attnum); - result = 0; /* keep compiler quiet */ -*************** heap_form_tuple(TupleDesc tupleDescripto -*** 722,727 **** ---- 727,734 ---- - - if (tupleDescriptor->tdhasoid) - len += sizeof(Oid); -+ if (tupleDescriptor->tdhassecid) -+ len += sizeof(Oid); - - hoff = len = MAXALIGN(len); /* align user data safely */ - -*************** heap_form_tuple(TupleDesc tupleDescripto -*** 753,758 **** ---- 760,767 ---- - - if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ - td->t_infomask = HEAP_HASOID; -+ if (tupleDescriptor->tdhassecid) -+ td->t_infomask |= HEAP_HASSECID; - - heap_fill_tuple(tupleDescriptor, - values, -*************** heap_modify_tuple(HeapTuple tuple, -*** 864,869 **** ---- 873,880 ---- - newTuple->t_tableOid = tuple->t_tableOid; - if (tupleDesc->tdhasoid) - HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); -+ if (HeapTupleHasSecid(newTuple)) -+ HeapTupleSetSecid(newTuple, HeapTupleGetSecid(tuple)); - - return newTuple; - } -*************** heap_form_minimal_tuple(TupleDesc tupleD -*** 1474,1479 **** ---- 1485,1492 ---- - - if (tupleDescriptor->tdhasoid) - len += sizeof(Oid); -+ if (tupleDescriptor->tdhassecid) -+ len += sizeof(Oid); - - hoff = len = MAXALIGN(len); /* align user data safely */ - -*************** heap_form_minimal_tuple(TupleDesc tupleD -*** 1495,1500 **** ---- 1508,1515 ---- - - if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ - tuple->t_infomask = HEAP_HASOID; -+ if (tupleDescriptor->tdhassecid) -+ tuple->t_infomask |= HEAP_HASSECID; - - heap_fill_tuple(tupleDescriptor, - values, -diff -Nrpc base/src/backend/access/common/tupdesc.c sepgsql/src/backend/access/common/tupdesc.c -*** base/src/backend/access/common/tupdesc.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/access/common/tupdesc.c Wed Sep 9 13:14:37 2009 -*************** CreateTemplateTupleDesc(int natts, bool -*** 88,93 **** ---- 88,94 ---- - desc->tdtypeid = RECORDOID; - desc->tdtypmod = -1; - desc->tdhasoid = hasoid; -+ desc->tdhassecid = false; - desc->tdrefcount = -1; /* assume not reference-counted */ - - return desc; -*************** CreateTupleDesc(int natts, bool hasoid, -*** 121,126 **** ---- 122,128 ---- - desc->tdtypeid = RECORDOID; - desc->tdtypmod = -1; - desc->tdhasoid = hasoid; -+ desc->tdhassecid = false; - desc->tdrefcount = -1; /* assume not reference-counted */ - - return desc; -*************** CreateTupleDescCopy(TupleDesc tupdesc) -*** 150,155 **** ---- 152,158 ---- - - desc->tdtypeid = tupdesc->tdtypeid; - desc->tdtypmod = tupdesc->tdtypmod; -+ desc->tdhassecid = tupdesc->tdhassecid; - - return desc; - } -*************** CreateTupleDescCopyConstr(TupleDesc tupd -*** 208,213 **** ---- 211,217 ---- - - desc->tdtypeid = tupdesc->tdtypeid; - desc->tdtypmod = tupdesc->tdtypmod; -+ desc->tdhassecid = tupdesc->tdhassecid; - - return desc; - } -*************** equalTupleDescs(TupleDesc tupdesc1, Tupl -*** 314,319 **** ---- 318,325 ---- - return false; - if (tupdesc1->tdhasoid != tupdesc2->tdhasoid) - return false; -+ if (tupdesc1->tdhassecid != tupdesc2->tdhassecid) -+ return false; - - for (i = 0; i < tupdesc1->natts; i++) - { -diff -Nrpc base/src/backend/access/heap/heapam.c sepgsql/src/backend/access/heap/heapam.c -*** base/src/backend/access/heap/heapam.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/access/heap/heapam.c Tue Dec 8 14:53:28 2009 -*************** -*** 54,59 **** ---- 54,60 ---- - #include "catalog/namespace.h" - #include "miscadmin.h" - #include "pgstat.h" -+ #include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/freespace.h" - #include "storage/lmgr.h" -*************** heap_insert(Relation relation, HeapTuple -*** 2016,2021 **** ---- 2017,2028 ---- - Oid - simple_heap_insert(Relation relation, HeapTuple tup) - { -+ /* -+ * SELinux assigns default security label for the tuple, -+ * but does not check permissions to the internal operations. -+ */ -+ sepgsqlHeapTupleInsert(relation, tup, true); -+ - return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL); - } - -*************** l2: -*** 2558,2563 **** ---- 2565,2575 ---- - Assert(!(newtup->t_data->t_infomask & HEAP_HASOID)); - } - -+ /* Preserve SecurityId, if not changed */ -+ if (HeapTupleHasSecid(newtup) && -+ !OidIsValid(HeapTupleGetSecid(newtup))) -+ HeapTupleSetSecid(newtup, HeapTupleGetSecid(&oldtup)); -+ - newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK); - newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); - newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED); -diff -Nrpc base/src/backend/access/heap/tuptoaster.c sepgsql/src/backend/access/heap/tuptoaster.c -*** base/src/backend/access/heap/tuptoaster.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/access/heap/tuptoaster.c Tue Sep 8 23:55:48 2009 -*************** toast_insert_or_update(Relation rel, Hea -*** 591,596 **** ---- 591,598 ---- - hoff += BITMAPLEN(numAttrs); - if (newtup->t_data->t_infomask & HEAP_HASOID) - hoff += sizeof(Oid); -+ if (HeapTupleHasSecid(newtup)) -+ hoff += sizeof(Oid); - hoff = MAXALIGN(hoff); - Assert(hoff == newtup->t_data->t_hoff); - /* now convert to a limit on the tuple data size */ -*************** toast_insert_or_update(Relation rel, Hea -*** 864,869 **** ---- 866,873 ---- - new_len += BITMAPLEN(numAttrs); - if (olddata->t_infomask & HEAP_HASOID) - new_len += sizeof(Oid); -+ if (HeapTupleHeaderHasSecid(olddata)) -+ new_len += sizeof(Oid); - new_len = MAXALIGN(new_len); - Assert(new_len == olddata->t_hoff); - new_data_len = heap_compute_data_size(tupleDesc, -*************** toast_flatten_tuple_attribute(Datum valu -*** 1015,1020 **** ---- 1019,1026 ---- - new_len += BITMAPLEN(numAttrs); - if (olddata->t_infomask & HEAP_HASOID) - new_len += sizeof(Oid); -+ if (HeapTupleHeaderHasSecid(olddata)) -+ new_len += sizeof(Oid); - new_len = MAXALIGN(new_len); - Assert(new_len == olddata->t_hoff); - new_data_len = heap_compute_data_size(tupleDesc, -*************** toast_save_datum(Relation rel, Datum val -*** 1213,1218 **** ---- 1219,1230 ---- - memcpy(VARDATA(&chunk_data), data_p, chunk_size); - toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull); - -+ /* -+ * NOTE: SE-PostgreSQL does not assign any security label -+ * for tuples within the TOASTVALUE relation, so we omit -+ * to put sepgsqlHeapTupleInsert() hook here. -+ */ -+ - heap_insert(toastrel, toasttup, mycid, options, NULL); - - /* -diff -Nrpc base/src/backend/access/transam/xact.c sepgsql/src/backend/access/transam/xact.c -*** base/src/backend/access/transam/xact.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/backend/access/transam/xact.c Tue Dec 15 17:30:25 2009 -*************** -*** 36,41 **** ---- 36,43 ---- - #include "libpq/be-fsstubs.h" - #include "miscadmin.h" - #include "pgstat.h" -+ #include "security/rowlevel.h" -+ #include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/fd.h" - #include "storage/lmgr.h" -*************** typedef struct TransactionStateData -*** 140,145 **** ---- 142,148 ---- - Oid prevUser; /* previous CurrentUserId setting */ - int prevSecContext; /* previous SecurityRestrictionContext */ - bool prevXactReadOnly; /* entry-time xact r/o state */ -+ int prevRowlv; /* previous Row-level control behavior */ - struct TransactionStateData *parent; /* back link to parent */ - } TransactionStateData; - -*************** static TransactionStateData TopTransacti -*** 168,173 **** ---- 171,177 ---- - InvalidOid, /* previous CurrentUserId setting */ - 0, /* previous SecurityRestrictionContext */ - false, /* entry-time xact r/o state */ -+ ROWLV_FILTER_MODE, /* previous Row-level control behavior */ - NULL /* link to parent state block */ - }; - -*************** StartTransaction(void) -*** 1524,1529 **** ---- 1528,1534 ---- - s->nChildXids = 0; - s->maxChildXids = 0; - GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); -+ s->prevRowlv = rowlvGetPerformingMode(); - /* SecurityRestrictionContext should never be set outside a transaction */ - Assert(s->prevSecContext == 0); - -*************** AbortTransaction(void) -*** 2031,2036 **** ---- 2036,2051 ---- - SetUserIdAndSecContext(s->prevUser, s->prevSecContext); - - /* -+ * Reset behavior of row-level access controls -+ */ -+ rowlvSetPerformingMode(s->prevRowlv); -+ -+ /* -+ * Reset access vector cache on error -+ */ -+ sepgsqlAvcReset(); -+ -+ /* - * do abort processing - */ - AfterTriggerEndXact(false); -*************** AbortSubTransaction(void) -*** 3874,3879 **** ---- 3889,3904 ---- - SetUserIdAndSecContext(s->prevUser, s->prevSecContext); - - /* -+ * Reset behavior of row-level access controls -+ */ -+ rowlvSetPerformingMode(s->prevRowlv); -+ -+ /* -+ * Reset access vector cache on error -+ */ -+ sepgsqlAvcReset(); -+ -+ /* - * We can skip all this stuff if the subxact failed before creating a - * ResourceOwner... - */ -*************** PushTransaction(void) -*** 4015,4020 **** ---- 4040,4046 ---- - s->blockState = TBLOCK_SUBBEGIN; - GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); - s->prevXactReadOnly = XactReadOnly; -+ s->prevRowlv = rowlvGetPerformingMode(); - - CurrentTransactionState = s; - -diff -Nrpc base/src/backend/bootstrap/bootparse.y sepgsql/src/backend/bootstrap/bootparse.y -*** base/src/backend/bootstrap/bootparse.y Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/bootstrap/bootparse.y Thu Oct 8 09:29:32 2009 -*************** -*** 42,47 **** ---- 42,48 ---- - #include "nodes/pg_list.h" - #include "nodes/primnodes.h" - #include "rewrite/prs2lock.h" -+ #include "security/sepgsql.h" - #include "storage/block.h" - #include "storage/fd.h" - #include "storage/ipc.h" -*************** Boot_CreateStmt: -*** 211,216 **** ---- 212,224 ---- - else - { - Oid id; -+ Oid *secLabels = -+ sepgsql_relation_create(LexIDStr($5), -+ RELKIND_RELATION, -+ tupdesc, -+ PG_CATALOG_NAMESPACE, -+ NULL, NIL, -+ false, false); - - id = heap_create_with_catalog(LexIDStr($5), - PG_CATALOG_NAMESPACE, -*************** Boot_CreateStmt: -*** 225,231 **** - 0, - ONCOMMIT_NOOP, - (Datum) 0, -! true); - elog(DEBUG4, "relation created with oid %u", id); - } - do_end(); ---- 233,240 ---- - 0, - ONCOMMIT_NOOP, - (Datum) 0, -! true, -! secLabels); - elog(DEBUG4, "relation created with oid %u", id); - } - do_end(); -diff -Nrpc base/src/backend/bootstrap/bootstrap.c sepgsql/src/backend/bootstrap/bootstrap.c -*** base/src/backend/bootstrap/bootstrap.c Fri Feb 20 22:15:36 2009 ---- sepgsql/src/backend/bootstrap/bootstrap.c Tue Dec 8 14:04:25 2009 -*************** -*** 26,37 **** ---- 26,39 ---- - #include "access/xact.h" - #include "bootstrap/bootstrap.h" - #include "catalog/index.h" -+ #include "catalog/pg_security.h" - #include "catalog/pg_type.h" - #include "libpq/pqsignal.h" - #include "miscadmin.h" - #include "nodes/makefuncs.h" - #include "postmaster/bgwriter.h" - #include "postmaster/walwriter.h" -+ #include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/ipc.h" - #include "storage/proc.h" -*************** AuxiliaryProcessMain(int argc, char *arg -*** 338,343 **** ---- 340,350 ---- - case WalWriterProcess: - statmsg = "wal writer process"; - break; -+ #ifdef HAVE_SELINUX -+ case SelinuxReceiverProcess: -+ statmsg = "selinux netlink receiver"; -+ break; -+ #endif - default: - statmsg = "??? process"; - break; -*************** AuxiliaryProcessMain(int argc, char *arg -*** 430,435 **** ---- 437,448 ---- - WalWriterMain(); - proc_exit(1); /* should never return */ - -+ #ifdef HAVE_SELINUX -+ case SelinuxReceiverProcess: -+ sepgsqlReceiverMain(); -+ proc_exit(1); /* should nener return */ -+ #endif -+ - default: - elog(PANIC, "unrecognized process type: %d", auxType); - proc_exit(1); -*************** BootstrapModeMain(void) -*** 497,502 **** ---- 510,520 ---- - */ - boot_yyparse(); - -+ /* -+ * Flush all the cached security label -+ */ -+ securityPostBootstrapingMode(); -+ - /* Perform a checkpoint to ensure everything's down to disk */ - SetProcessingMode(NormalProcessing); - CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); -*************** InsertOneTuple(Oid objectid) -*** 794,799 **** ---- 812,819 ---- - tupDesc = CreateTupleDesc(numattr, - RelationGetForm(boot_reldesc)->relhasoids, - attrtypes); -+ tupDesc->tdhassecid = RelationGetDescr(boot_reldesc)->tdhassecid; -+ - tuple = heap_form_tuple(tupDesc, values, Nulls); - if (objectid != (Oid) 0) - HeapTupleSetOid(tuple, objectid); -diff -Nrpc base/src/backend/catalog/Makefile sepgsql/src/backend/catalog/Makefile -*** base/src/backend/catalog/Makefile Wed May 13 11:30:07 2009 ---- sepgsql/src/backend/catalog/Makefile Wed Jul 15 19:30:50 2009 -*************** include $(top_builddir)/src/Makefile.glo -*** 13,19 **** - OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ - pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ - pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ -! pg_shdepend.o pg_type.o storage.o toasting.o - - BKIFILES = postgres.bki postgres.description postgres.shdescription - ---- 13,19 ---- - OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ - pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ - pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ -! pg_security.o pg_shdepend.o pg_type.o storage.o toasting.o - - BKIFILES = postgres.bki postgres.description postgres.shdescription - -*************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr -*** 34,40 **** - pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ - pg_database.h pg_tablespace.h pg_pltemplate.h \ - pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ -! pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ - pg_ts_parser.h pg_ts_template.h \ - pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ - toasting.h indexing.h \ ---- 34,40 ---- - pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ - pg_database.h pg_tablespace.h pg_pltemplate.h \ - pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ -! pg_security.h pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ - pg_ts_parser.h pg_ts_template.h \ - pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ - toasting.h indexing.h \ -diff -Nrpc base/src/backend/catalog/aclchk.c sepgsql/src/backend/catalog/aclchk.c -*** base/src/backend/catalog/aclchk.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/aclchk.c Fri Sep 18 14:51:00 2009 -*************** -*** 35,40 **** ---- 35,41 ---- - #include "catalog/pg_operator.h" - #include "catalog/pg_opfamily.h" - #include "catalog/pg_proc.h" -+ #include "catalog/pg_security.h" - #include "catalog/pg_tablespace.h" - #include "catalog/pg_type.h" - #include "catalog/pg_ts_config.h" -*************** -*** 43,48 **** ---- 44,50 ---- - #include "foreign/foreign.h" - #include "miscadmin.h" - #include "parser/parse_func.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/fmgroids.h" - #include "utils/lsyscache.h" -*************** expand_all_col_privileges(Oid table_oid, -*** 672,677 **** ---- 674,685 ---- - if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) - continue; - -+ /* Skip OID column, if it doesn't exist */ -+ if (curr_att == SecurityAttributeNumber && -+ (classForm->relkind != RELKIND_RELATION || -+ table_oid == SecurityRelationId)) -+ continue; -+ - /* Views don't have any system columns at all */ - if (classForm->relkind == RELKIND_VIEW && curr_att < 0) - continue; -*************** ExecGrant_Attribute(InternalGrant *istmt -*** 774,779 **** ---- 782,789 ---- - relOid, grantorId, ACL_KIND_COLUMN, - relname, attnum, - NameStr(pg_attribute_tuple->attname)); -+ /* SELinux checks */ -+ sepgsql_attribute_grant(relOid, attnum); - - /* - * Generate new ACL. -*************** ExecGrant_Relation(InternalGrant *istmt) -*** 1029,1034 **** ---- 1039,1046 ---- - ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, - NameStr(pg_class_tuple->relname), - 0, NULL); -+ /* SELinux checks */ -+ sepgsql_relation_grant(relOid); - - /* - * Generate new ACL. -*************** ExecGrant_Database(InternalGrant *istmt) -*** 1217,1222 **** ---- 1229,1236 ---- - datId, grantorId, ACL_KIND_DATABASE, - NameStr(pg_database_tuple->datname), - 0, NULL); -+ /* SELinux permission checks */ -+ sepgsql_database_grant(datId); - - /* - * Generate new ACL. -*************** ExecGrant_Fdw(InternalGrant *istmt) -*** 1335,1340 **** ---- 1349,1356 ---- - fdwid, grantorId, ACL_KIND_FDW, - NameStr(pg_fdw_tuple->fdwname), - 0, NULL); -+ /* SELinux permission checks */ -+ sepgsql_fdw_grant(fdwid); - - /* - * Generate new ACL. -*************** ExecGrant_ForeignServer(InternalGrant *i -*** 1454,1459 **** ---- 1470,1477 ---- - srvid, grantorId, ACL_KIND_FOREIGN_SERVER, - NameStr(pg_server_tuple->srvname), - 0, NULL); -+ /* SELinux checks */ -+ sepgsql_foreign_server_grant(srvid); - - /* - * Generate new ACL. -*************** ExecGrant_Function(InternalGrant *istmt) -*** 1572,1577 **** ---- 1590,1597 ---- - funcId, grantorId, ACL_KIND_PROC, - NameStr(pg_proc_tuple->proname), - 0, NULL); -+ /* SELinux: db_procedure:{setattr} */ -+ sepgsql_proc_grant(funcId); - - /* - * Generate new ACL. -*************** ExecGrant_Language(InternalGrant *istmt) -*** 1696,1701 **** ---- 1716,1723 ---- - langId, grantorId, ACL_KIND_LANGUAGE, - NameStr(pg_language_tuple->lanname), - 0, NULL); -+ /* SELinux checks */ -+ sepgsql_language_grant(langId); - - /* - * Generate new ACL. -*************** ExecGrant_Namespace(InternalGrant *istmt -*** 1815,1820 **** ---- 1837,1845 ---- - NameStr(pg_namespace_tuple->nspname), - 0, NULL); - -+ /* SELinux: db_schema:{setattr} */ -+ sepgsql_schema_grant(nspid); -+ - /* - * Generate new ACL. - * -diff -Nrpc base/src/backend/catalog/catalog.c sepgsql/src/backend/catalog/catalog.c -*** base/src/backend/catalog/catalog.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/catalog.c Wed Jul 15 19:30:50 2009 -*************** -*** 31,36 **** ---- 31,37 ---- - #include "catalog/pg_database.h" - #include "catalog/pg_namespace.h" - #include "catalog/pg_pltemplate.h" -+ #include "catalog/pg_security.h" - #include "catalog/pg_shdepend.h" - #include "catalog/pg_shdescription.h" - #include "catalog/pg_tablespace.h" -*************** IsSharedRelation(Oid relationId) -*** 304,309 **** ---- 305,311 ---- - relationId == AuthMemRelationId || - relationId == DatabaseRelationId || - relationId == PLTemplateRelationId || -+ relationId == SecurityRelationId || - relationId == SharedDescriptionRelationId || - relationId == SharedDependRelationId || - relationId == TableSpaceRelationId) -*************** IsSharedRelation(Oid relationId) -*** 316,321 **** ---- 318,325 ---- - relationId == DatabaseNameIndexId || - relationId == DatabaseOidIndexId || - relationId == PLTemplateNameIndexId || -+ relationId == SecuritySecidIndexId || -+ relationId == SecuritySecattrIndexId || - relationId == SharedDescriptionObjIndexId || - relationId == SharedDependDependerIndexId || - relationId == SharedDependReferenceIndexId || -*************** IsSharedRelation(Oid relationId) -*** 327,332 **** ---- 331,338 ---- - relationId == PgAuthidToastIndex || - relationId == PgDatabaseToastTable || - relationId == PgDatabaseToastIndex || -+ relationId == PgSecurityToastTable || -+ relationId == PgSecurityToastIndex || - relationId == PgShdescriptionToastTable || - relationId == PgShdescriptionToastIndex) - return true; -diff -Nrpc base/src/backend/catalog/dependency.c sepgsql/src/backend/catalog/dependency.c -*** base/src/backend/catalog/dependency.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/backend/catalog/dependency.c Tue Dec 15 17:30:25 2009 -*************** -*** 63,68 **** ---- 63,69 ---- - #include "nodes/nodeFuncs.h" - #include "parser/parsetree.h" - #include "rewrite/rewriteRemove.h" -+ #include "security/sepgsql.h" - #include "storage/lmgr.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -*************** static void reportDependentObjects(const -*** 160,166 **** - DropBehavior behavior, - int msglevel, - const ObjectAddress *origObject); -! static void deleteOneObject(const ObjectAddress *object, Relation depRel); - static void doDeletion(const ObjectAddress *object); - static void AcquireDeletionLock(const ObjectAddress *object); - static void ReleaseDeletionLock(const ObjectAddress *object); ---- 161,168 ---- - DropBehavior behavior, - int msglevel, - const ObjectAddress *origObject); -! static void deleteOneObject(const ObjectAddress *object, -! Relation depRel, bool permission); - static void doDeletion(const ObjectAddress *object); - static void AcquireDeletionLock(const ObjectAddress *object); - static void ReleaseDeletionLock(const ObjectAddress *object); -*************** static void getOpFamilyDescription(Strin -*** 192,200 **** - * are variants on the same theme; if you change anything here you'll likely - * need to fix them too. - */ -! void -! performDeletion(const ObjectAddress *object, -! DropBehavior behavior) - { - Relation depRel; - ObjectAddresses *targetObjects; ---- 194,202 ---- - * are variants on the same theme; if you change anything here you'll likely - * need to fix them too. - */ -! static void -! performDeletionInternal(const ObjectAddress *object, -! DropBehavior behavior, bool permission) - { - Relation depRel; - ObjectAddresses *targetObjects; -*************** performDeletion(const ObjectAddress *obj -*** 240,246 **** - { - ObjectAddress *thisobj = targetObjects->refs + i; - -! deleteOneObject(thisobj, depRel); - } - - /* And clean up */ ---- 242,248 ---- - { - ObjectAddress *thisobj = targetObjects->refs + i; - -! deleteOneObject(thisobj, depRel, permission); - } - - /* And clean up */ -*************** performDeletion(const ObjectAddress *obj -*** 249,254 **** ---- 251,268 ---- - heap_close(depRel, RowExclusiveLock); - } - -+ void -+ performDeletion(const ObjectAddress *object, DropBehavior behavior) -+ { -+ performDeletionInternal(object, behavior, true); -+ } -+ -+ void -+ performDeletionNoPerms(const ObjectAddress *object, DropBehavior behavior) -+ { -+ performDeletionInternal(object, behavior, false); -+ } -+ - /* - * performMultipleDeletions: Similar to performDeletion, but act on multiple - * objects at once. -*************** performMultipleDeletions(const ObjectAdd -*** 322,328 **** - { - ObjectAddress *thisobj = targetObjects->refs + i; - -! deleteOneObject(thisobj, depRel); - } - - /* And clean up */ ---- 336,343 ---- - { - ObjectAddress *thisobj = targetObjects->refs + i; - -! /* currently, all the caller path need permission checks */ -! deleteOneObject(thisobj, depRel, true); - } - - /* And clean up */ -*************** deleteWhatDependsOn(const ObjectAddress -*** 393,399 **** - if (thisextra->flags & DEPFLAG_ORIGINAL) - continue; - -! deleteOneObject(thisobj, depRel); - } - - /* And clean up */ ---- 408,414 ---- - if (thisextra->flags & DEPFLAG_ORIGINAL) - continue; - -! deleteOneObject(thisobj, depRel, false); - } - - /* And clean up */ -*************** reportDependentObjects(const ObjectAddre -*** 943,955 **** - * 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.) ---- 958,974 ---- - * depRel is the already-open pg_depend relation. - */ - static void -! deleteOneObject(const ObjectAddress *object, Relation depRel, bool permission) - { - ScanKeyData key[3]; - int nkeys; - SysScanDesc scan; - HeapTuple tup; - -+ /* SELinux checks db_xxx:{drop}, if necessary */ -+ if (permission) -+ sepgsql_sysobj_drop(object); -+ - /* - * First remove any pg_depend records that link from this object to - * others. (Any records linking to this object should be gone already.) -diff -Nrpc base/src/backend/catalog/heap.c sepgsql/src/backend/catalog/heap.c -*** base/src/backend/catalog/heap.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/heap.c Wed Sep 9 16:47:01 2009 -*************** -*** 43,48 **** ---- 43,49 ---- - #include "catalog/pg_constraint.h" - #include "catalog/pg_inherits.h" - #include "catalog/pg_namespace.h" -+ #include "catalog/pg_security.h" - #include "catalog/pg_statistic.h" - #include "catalog/pg_tablespace.h" - #include "catalog/pg_type.h" -*************** -*** 56,61 **** ---- 57,63 ---- - #include "parser/parse_coerce.h" - #include "parser/parse_expr.h" - #include "parser/parse_relation.h" -+ #include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/freespace.h" - #include "storage/smgr.h" -*************** static void AddNewRelationTuple(Relation -*** 74,80 **** - Oid new_rel_oid, Oid new_type_oid, - Oid relowner, - char relkind, -! Datum reloptions); - static Oid AddNewRelationType(const char *typeName, - Oid typeNamespace, - Oid new_rel_oid, ---- 76,83 ---- - Oid new_rel_oid, Oid new_type_oid, - Oid relowner, - char relkind, -! Datum reloptions, -! Oid *secLabels); - static Oid AddNewRelationType(const char *typeName, - Oid typeNamespace, - Oid new_rel_oid, -*************** static FormData_pg_attribute a7 = { -*** 158,164 **** - true, 'p', 'i', true, false, false, true, 0, {0} - }; - -! static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; - - /* - * This function returns a Form_pg_attribute pointer for a system attribute. ---- 161,176 ---- - true, 'p', 'i', true, false, false, true, 0, {0} - }; - -! /* -! * System columns for enhanced security features -! */ -! static FormData_pg_attribute a8 = { -! 0, {SecurityAttributeName}, TEXTOID, 0, -1, -! SecurityAttributeNumber, 0, -1, -1, -! false, 'x', 'i', true, false, false, true, 0, {0} -! }; -! -! static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8}; - - /* - * This function returns a Form_pg_attribute pointer for a system attribute. -*************** SystemAttributeByName(const char *attnam -*** 198,203 **** ---- 210,226 ---- - return NULL; - } - -+ /* -+ * If the given attribute number is writable, returns true. -+ */ -+ bool -+ SystemAttributeIsWritable(AttrNumber attnum) -+ { -+ if (attnum == SecurityAttributeNumber) -+ return true; -+ -+ return false; -+ } - - /* ---------------------------------------------------------------- - * XXX END OF UGLY HARD CODED BADNESS XXX -*************** heap_create(const char *relname, -*** 293,298 **** ---- 316,326 ---- - relid, - reltablespace, - shared_relation); -+ /* -+ * Does the relation have security attribute? -+ */ -+ RelationGetDescr(rel)->tdhassecid -+ = securityTupleDescHasSecid(relid, relkind); - - /* - * Have the storage manager create the relation's disk file, if needed. -*************** CheckAttributeType(const char *attname, -*** 487,493 **** - void - InsertPgAttributeTuple(Relation pg_attribute_rel, - Form_pg_attribute new_attribute, -! CatalogIndexState indstate) - { - Datum values[Natts_pg_attribute]; - bool nulls[Natts_pg_attribute]; ---- 515,522 ---- - void - InsertPgAttributeTuple(Relation pg_attribute_rel, - Form_pg_attribute new_attribute, -! CatalogIndexState indstate, -! Oid new_att_secid) - { - Datum values[Natts_pg_attribute]; - bool nulls[Natts_pg_attribute]; -*************** InsertPgAttributeTuple(Relation pg_attri -*** 520,525 **** ---- 549,557 ---- - - tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls); - -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, new_att_secid); -+ - /* finally insert the new tuple, update the indexes, and clean up */ - simple_heap_insert(pg_attribute_rel, tup); - -*************** AddNewAttributeTuples(Oid new_rel_oid, -*** 543,555 **** - TupleDesc tupdesc, - char relkind, - bool oidislocal, -! int oidinhcount) - { - Form_pg_attribute attr; - int i; - Relation rel; - CatalogIndexState indstate; - int natts = tupdesc->natts; - ObjectAddress myself, - referenced; - ---- 575,589 ---- - TupleDesc tupdesc, - char relkind, - bool oidislocal, -! int oidinhcount, -! Oid *secLabels) - { - Form_pg_attribute attr; - int i; - Relation rel; - CatalogIndexState indstate; - int natts = tupdesc->natts; -+ Oid new_att_secid; - ObjectAddress myself, - referenced; - -*************** AddNewAttributeTuples(Oid new_rel_oid, -*** 573,579 **** - attr->attstattarget = -1; - attr->attcacheoff = -1; - -! InsertPgAttributeTuple(rel, attr, indstate); - - /* Add dependency info */ - myself.classId = RelationRelationId; ---- 607,617 ---- - attr->attstattarget = -1; - attr->attcacheoff = -1; - -! /* Security label of the column */ -! new_att_secid = (!secLabels ? InvalidOid -! : secLabels[i - FirstLowInvalidHeapAttributeNumber]); -! -! InsertPgAttributeTuple(rel, attr, indstate, new_att_secid); - - /* Add dependency info */ - myself.classId = RelationRelationId; -*************** AddNewAttributeTuples(Oid new_rel_oid, -*** 601,606 **** ---- 639,650 ---- - SysAtt[i]->attnum == ObjectIdAttributeNumber) - continue; - -+ /* skip Secid where appropriate */ -+ if (SysAtt[i]->attnum == SecurityAttributeNumber && -+ (relkind != RELKIND_RELATION || -+ new_rel_oid == SecurityRelationId)) -+ continue; -+ - memcpy(&attStruct, (char *) SysAtt[i], sizeof(FormData_pg_attribute)); - - /* Fill in the correct relation OID in the copied tuple */ -*************** AddNewAttributeTuples(Oid new_rel_oid, -*** 613,619 **** - attStruct.attinhcount = oidinhcount; - } - -! InsertPgAttributeTuple(rel, &attStruct, indstate); - } - } - ---- 657,667 ---- - attStruct.attinhcount = oidinhcount; - } - -! /* Security label of the system column */ -! new_att_secid = (!secLabels ? InvalidOid -! : secLabels[SysAtt[i]->attnum - FirstLowInvalidHeapAttributeNumber]); -! -! InsertPgAttributeTuple(rel, &attStruct, indstate, new_att_secid); - } - } - -*************** void -*** 641,647 **** - InsertPgClassTuple(Relation pg_class_desc, - Relation new_rel_desc, - Oid new_rel_oid, -! Datum reloptions) - { - Form_pg_class rd_rel = new_rel_desc->rd_rel; - Datum values[Natts_pg_class]; ---- 689,696 ---- - InsertPgClassTuple(Relation pg_class_desc, - Relation new_rel_desc, - Oid new_rel_oid, -! Datum reloptions, -! Oid new_rel_secid) - { - Form_pg_class rd_rel = new_rel_desc->rd_rel; - Datum values[Natts_pg_class]; -*************** InsertPgClassTuple(Relation pg_class_des -*** 690,695 **** ---- 739,747 ---- - */ - HeapTupleSetOid(tup, new_rel_oid); - -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, new_rel_secid); -+ - /* finally insert the new tuple, update the indexes, and clean up */ - simple_heap_insert(pg_class_desc, tup); - -*************** AddNewRelationTuple(Relation pg_class_de -*** 712,720 **** - Oid new_type_oid, - Oid relowner, - char relkind, -! Datum reloptions) - { - Form_pg_class new_rel_reltup; - - /* - * first we update some of the information in our uncataloged relation's ---- 764,774 ---- - Oid new_type_oid, - Oid relowner, - char relkind, -! Datum reloptions, -! Oid *secLabels) - { - Form_pg_class new_rel_reltup; -+ Oid new_rel_secid = InvalidOid; - - /* - * first we update some of the information in our uncataloged relation's -*************** AddNewRelationTuple(Relation pg_class_de -*** 771,778 **** - - new_rel_desc->rd_att->tdtypeid = new_type_oid; - - /* Now build and insert the tuple */ -! InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions); - } - - ---- 825,836 ---- - - new_rel_desc->rd_att->tdtypeid = new_type_oid; - -+ if (secLabels) -+ new_rel_secid = secLabels[0]; -+ - /* Now build and insert the tuple */ -! InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, -! reloptions, new_rel_secid); - } - - -*************** heap_create_with_catalog(const char *rel -*** 843,849 **** - int oidinhcount, - OnCommitAction oncommit, - Datum reloptions, -! bool allow_system_table_mods) - { - Relation pg_class_desc; - Relation new_rel_desc; ---- 901,908 ---- - int oidinhcount, - OnCommitAction oncommit, - Datum reloptions, -! bool allow_system_table_mods, -! Oid *secLabels) - { - Relation pg_class_desc; - Relation new_rel_desc; -*************** heap_create_with_catalog(const char *rel -*** 1019,1031 **** - new_type_oid, - ownerid, - relkind, -! reloptions); - - /* - * now add tuples to pg_attribute for the attributes in our new relation. - */ - AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, -! oidislocal, oidinhcount); - - /* - * Make a dependency link to force the relation to be deleted if its ---- 1078,1091 ---- - new_type_oid, - ownerid, - relkind, -! reloptions, -! secLabels); - - /* - * now add tuples to pg_attribute for the attributes in our new relation. - */ - AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, -! oidislocal, oidinhcount, secLabels); - - /* - * Make a dependency link to force the relation to be deleted if its -*************** heap_drop_with_catalog(Oid relid) -*** 1484,1489 **** ---- 1544,1554 ---- - * delete relation tuple - */ - DeleteRelationTuple(relid); -+ -+ /* -+ * delete orphan pg_security entries -+ */ -+ securityReclaimOnDropTable(relid); - } - - -diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c -*** base/src/backend/catalog/index.c 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 base/src/backend/catalog/namespace.c sepgsql/src/backend/catalog/namespace.c -*** base/src/backend/catalog/namespace.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/namespace.c Thu Sep 17 17:04:16 2009 -*************** -*** 39,44 **** ---- 39,45 ---- - #include "miscadmin.h" - #include "nodes/makefuncs.h" - #include "parser/parse_func.h" -+ #include "security/sepgsql.h" - #include "storage/backendid.h" - #include "storage/ipc.h" - #include "utils/acl.h" -*************** LookupExplicitNamespace(const char *nspn -*** 2105,2111 **** ---- 2106,2115 ---- - if (strcmp(nspname, "pg_temp") == 0) - { - if (OidIsValid(myTempNamespace)) -+ { -+ sepgsql_schema_search(myTempNamespace, true); - return myTempNamespace; -+ } - - /* - * Since this is used only for looking up existing objects, there is -*************** LookupExplicitNamespace(const char *nspn -*** 2127,2132 **** ---- 2131,2137 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - nspname); -+ sepgsql_schema_search(namespaceId, true); - - return namespaceId; - } -*************** recomputeNamespacePath(void) -*** 2722,2728 **** - if (OidIsValid(namespaceId) && - !list_member_oid(oidlist, namespaceId) && - pg_namespace_aclcheck(namespaceId, roleid, -! ACL_USAGE) == ACLCHECK_OK) - oidlist = lappend_oid(oidlist, namespaceId); - } - } ---- 2727,2734 ---- - if (OidIsValid(namespaceId) && - !list_member_oid(oidlist, namespaceId) && - pg_namespace_aclcheck(namespaceId, roleid, -! ACL_USAGE) == ACLCHECK_OK && -! sepgsql_schema_search(namespaceId, false)) - oidlist = lappend_oid(oidlist, namespaceId); - } - } -*************** recomputeNamespacePath(void) -*** 2731,2737 **** - /* pg_temp --- substitute temp namespace, if any */ - if (OidIsValid(myTempNamespace)) - { -! if (!list_member_oid(oidlist, myTempNamespace)) - oidlist = lappend_oid(oidlist, myTempNamespace); - } - else ---- 2737,2744 ---- - /* pg_temp --- substitute temp namespace, if any */ - if (OidIsValid(myTempNamespace)) - { -! if (!list_member_oid(oidlist, myTempNamespace) && -! sepgsql_schema_search(myTempNamespace, false)) - oidlist = lappend_oid(oidlist, myTempNamespace); - } - else -*************** recomputeNamespacePath(void) -*** 2750,2756 **** - if (OidIsValid(namespaceId) && - !list_member_oid(oidlist, namespaceId) && - pg_namespace_aclcheck(namespaceId, roleid, -! ACL_USAGE) == ACLCHECK_OK) - oidlist = lappend_oid(oidlist, namespaceId); - } - } ---- 2757,2764 ---- - if (OidIsValid(namespaceId) && - !list_member_oid(oidlist, namespaceId) && - pg_namespace_aclcheck(namespaceId, roleid, -! ACL_USAGE) == ACLCHECK_OK && -! sepgsql_schema_search(namespaceId, false)) - oidlist = lappend_oid(oidlist, namespaceId); - } - } -*************** InitTempTableNamespace(void) -*** 2816,2821 **** ---- 2824,2830 ---- - char namespaceName[NAMEDATALEN]; - Oid namespaceId; - Oid toastspaceId; -+ Oid nspsecid; - - Assert(!OidIsValid(myTempNamespace)); - -*************** InitTempTableNamespace(void) -*** 2836,2841 **** ---- 2845,2853 ---- - errmsg("permission denied to create temporary tables in database \"%s\"", - get_database_name(MyDatabaseId)))); - -+ /* SELinux checks permission to create temp schema */ -+ nspsecid = sepgsql_schema_create(namespaceName, true, NULL); -+ - snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); - - namespaceId = GetSysCacheOid(NAMESPACENAME, -*************** InitTempTableNamespace(void) -*** 2851,2857 **** - * temp tables. This works because the places that access the temp - * namespace for my own backend skip permissions checks on it. - */ -! namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); - /* Advance command counter to make namespace visible */ - CommandCounterIncrement(); - } ---- 2863,2871 ---- - * temp tables. This works because the places that access the temp - * namespace for my own backend skip permissions checks on it. - */ -! namespaceId = NamespaceCreate(namespaceName, -! BOOTSTRAP_SUPERUSERID, -! nspsecid); - /* Advance command counter to make namespace visible */ - CommandCounterIncrement(); - } -*************** InitTempTableNamespace(void) -*** 2877,2883 **** - 0, 0, 0); - if (!OidIsValid(toastspaceId)) - { -! toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); - /* Advance command counter to make namespace visible */ - CommandCounterIncrement(); - } ---- 2891,2899 ---- - 0, 0, 0); - if (!OidIsValid(toastspaceId)) - { -! toastspaceId = NamespaceCreate(namespaceName, -! BOOTSTRAP_SUPERUSERID, -! nspsecid); - /* Advance command counter to make namespace visible */ - CommandCounterIncrement(); - } -*************** RemoveTempRelations(Oid tempNamespaceId) -*** 3030,3035 **** ---- 3046,3058 ---- - object.objectId = tempNamespaceId; - object.objectSubId = 0; - -+ /* -+ * TODO: -+ * SELinux should not check db_xxx:{drop} permission during cleaning -+ * up all the temporary objects. It may be necessary a bool argument -+ * to control MAC permission check on deleteOneObject() called from -+ * deleteWhatDependsOn() and so on. -+ */ - deleteWhatDependsOn(&object, false); - } - -diff -Nrpc base/src/backend/catalog/pg_aggregate.c sepgsql/src/backend/catalog/pg_aggregate.c -*** base/src/backend/catalog/pg_aggregate.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/pg_aggregate.c Wed Jul 15 19:37:35 2009 -*************** AggregateCreate(const char *aggName, -*** 231,237 **** - NIL, /* parameterDefaults */ - PointerGetDatum(NULL), /* proconfig */ - 1, /* procost */ -! 0); /* prorows */ - - /* - * Okay to create the pg_aggregate entry. ---- 231,238 ---- - NIL, /* parameterDefaults */ - PointerGetDatum(NULL), /* proconfig */ - 1, /* procost */ -! 0, /* prorows */ -! NULL); /* proseclabel*/ - - /* - * Okay to create the pg_aggregate entry. -diff -Nrpc base/src/backend/catalog/pg_conversion.c sepgsql/src/backend/catalog/pg_conversion.c -*** base/src/backend/catalog/pg_conversion.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/catalog/pg_conversion.c Thu Sep 17 22:10:19 2009 -*************** Oid -*** 40,46 **** - ConversionCreate(const char *conname, Oid connamespace, - Oid conowner, - int32 conforencoding, int32 contoencoding, -! Oid conproc, bool def) - { - int i; - Relation rel; ---- 40,46 ---- - ConversionCreate(const char *conname, Oid connamespace, - Oid conowner, - int32 conforencoding, int32 contoencoding, -! Oid conproc, Oid consecid, bool def) - { - int i; - Relation rel; -*************** ConversionCreate(const char *conname, Oi -*** 104,109 **** ---- 104,111 ---- - values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def); - - tup = heap_form_tuple(tupDesc, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, consecid); - - /* insert a new tuple */ - oid = simple_heap_insert(rel, tup); -diff -Nrpc base/src/backend/catalog/pg_largeobject.c sepgsql/src/backend/catalog/pg_largeobject.c -*** base/src/backend/catalog/pg_largeobject.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/catalog/pg_largeobject.c Wed Jul 15 19:48:58 2009 -*************** -*** 18,23 **** ---- 18,24 ---- - #include "access/heapam.h" - #include "catalog/indexing.h" - #include "catalog/pg_largeobject.h" -+ #include "security/sepgsql.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" - #include "utils/rel.h" -*************** LargeObjectCreate(Oid loid) -*** 59,64 **** ---- 60,68 ---- - - ntup = heap_form_tuple(pg_largeobject->rd_att, values, nulls); - -+ /* SELinux checks db_blob:{create} */ -+ sepgsqlCheckBlobCreate(pg_largeobject, ntup); -+ - /* - * Insert it - */ -*************** LargeObjectDrop(Oid loid) -*** 93,98 **** ---- 97,105 ---- - - while ((tuple = systable_getnext(sd)) != NULL) - { -+ /* SELinux checks db_blob:{drop} */ -+ if (!found) -+ sepgsqlCheckBlobDrop(pg_largeobject, tuple); - simple_heap_delete(pg_largeobject, &tuple->t_self); - found = true; - } -diff -Nrpc base/src/backend/catalog/pg_namespace.c sepgsql/src/backend/catalog/pg_namespace.c -*** base/src/backend/catalog/pg_namespace.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/catalog/pg_namespace.c Tue Sep 8 23:55:48 2009 -*************** -*** 28,34 **** - * --------------- - */ - Oid -! NamespaceCreate(const char *nspName, Oid ownerId) - { - Relation nspdesc; - HeapTuple tup; ---- 28,34 ---- - * --------------- - */ - Oid -! NamespaceCreate(const char *nspName, Oid ownerId, Oid nspsecid) - { - Relation nspdesc; - HeapTuple tup; -*************** NamespaceCreate(const char *nspName, Oid -*** 66,71 **** ---- 66,73 ---- - tupDesc = nspdesc->rd_att; - - tup = heap_form_tuple(tupDesc, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, nspsecid); - - nspoid = simple_heap_insert(nspdesc, tup); - Assert(OidIsValid(nspoid)); -diff -Nrpc base/src/backend/catalog/pg_operator.c sepgsql/src/backend/catalog/pg_operator.c -*** base/src/backend/catalog/pg_operator.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/pg_operator.c Thu Sep 17 22:10:19 2009 -*************** -*** 28,33 **** ---- 28,34 ---- - #include "catalog/pg_type.h" - #include "miscadmin.h" - #include "parser/parse_oper.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -*************** OperatorShellMake(const char *operatorNa -*** 204,209 **** ---- 205,211 ---- - { - Relation pg_operator_desc; - Oid operatorObjectId; -+ Oid secid; - int i; - HeapTuple tup; - Datum values[Natts_pg_operator]; -*************** OperatorShellMake(const char *operatorNa -*** 220,225 **** ---- 222,231 ---- - errmsg("\"%s\" is not a valid operator name", - operatorName))); - -+ /* SELinux permission check */ -+ secid = sepgsql_operator_create(operatorName, InvalidOid, -+ operatorNamespace, -+ InvalidOid, InvalidOid, InvalidOid); - /* - * initialize our *nulls and *values arrays - */ -*************** OperatorShellMake(const char *operatorNa -*** 260,265 **** ---- 266,273 ---- - * create a new operator tuple - */ - tup = heap_form_tuple(tupDesc, values, nulls); -+ if (HeapTupleHasSecid(tup) && OidIsValid(secid)) -+ HeapTupleSetSecid(tup, secid); - - /* - * insert our "shell" operator tuple -*************** OperatorCreate(const char *operatorName, -*** 347,352 **** ---- 355,361 ---- - bool selfCommutator = false; - NameData oname; - TupleDesc tupDesc; -+ Oid secid; - int i; - - /* -*************** OperatorCreate(const char *operatorName, -*** 476,481 **** ---- 485,494 ---- - else - negatorId = InvalidOid; - -+ /* SELinux permission checks */ -+ secid = sepgsql_operator_create(operatorName, operatorObjectId, -+ operatorNamespace, -+ procedureId, restrictionId, joinId); - /* - * set up values in the operator tuple - */ -*************** OperatorCreate(const char *operatorName, -*** 523,528 **** ---- 536,543 ---- - values, - nulls, - replaces); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, secid); - - simple_heap_update(pg_operator_desc, &tup->t_self, tup); - } -*************** OperatorCreate(const char *operatorName, -*** 530,535 **** ---- 545,552 ---- - { - tupDesc = pg_operator_desc->rd_att; - tup = heap_form_tuple(tupDesc, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, secid); - - operatorObjectId = simple_heap_insert(pg_operator_desc, tup); - } -diff -Nrpc base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_proc.c -*** base/src/backend/catalog/pg_proc.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/backend/catalog/pg_proc.c Tue Dec 15 17:30:25 2009 -*************** -*** 29,34 **** ---- 29,35 ---- - #include "miscadmin.h" - #include "nodes/nodeFuncs.h" - #include "parser/parse_type.h" -+ #include "security/sepgsql.h" - #include "tcop/pquery.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" -*************** ProcedureCreate(const char *procedureNam -*** 78,84 **** - List *parameterDefaults, - Datum proconfig, - float4 procost, -! float4 prorows) - { - Oid retval; - int parameterCount; ---- 79,86 ---- - List *parameterDefaults, - Datum proconfig, - float4 procost, -! float4 prorows, -! Node *proseclabel) - { - Oid retval; - int parameterCount; -*************** ProcedureCreate(const char *procedureNam -*** 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 base/src/backend/catalog/pg_security.c sepgsql/src/backend/catalog/pg_security.c -*** base/src/backend/catalog/pg_security.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/catalog/pg_security.c Thu Sep 10 15:18:03 2009 -*************** -*** 0 **** ---- 1,637 ---- -+ /* -+ * src/backend/catalog/pg_security.c -+ * routines to support security label management -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+ #include "postgres.h" -+ -+ #include "access/genam.h" -+ #include "access/heapam.h" -+ #include "access/sysattr.h" -+ #include "access/xact.h" -+ #include "catalog/catalog.h" -+ #include "catalog/indexing.h" -+ #include "catalog/pg_proc.h" -+ #include "catalog/pg_security.h" -+ #include "catalog/pg_type.h" -+ #include "executor/spi.h" -+ #include "miscadmin.h" -+ #include "security/rowlevel.h" -+ #include "security/sepgsql.h" -+ #include "utils/builtins.h" -+ #include "utils/fmgroids.h" -+ #include "utils/memutils.h" -+ #include "utils/rel.h" -+ #include "utils/lsyscache.h" -+ #include "utils/syscache.h" -+ #include "utils/tqual.h" -+ -+ bool -+ securityTupleDescHasSecid(Oid relid, char relkind) -+ { -+ return sepgsqlTupleDescHasSecid(relid, relkind); -+ } -+ -+ /* -+ * security attribute management at the initdb phase. -+ */ -+ typedef struct earlySecAttr -+ { -+ struct earlySecAttr *next; -+ Oid secid; -+ Oid datid; -+ Oid relid; -+ char seckind; -+ char secattr[1]; -+ } earlySecAttr; -+ -+ static earlySecAttr *earlySecAttrList = NULL; -+ -+ static Oid -+ earlyInputSecurityAttr(Oid datid, Oid relid, char seckind, const char *secattr) -+ { -+ static Oid dummySecid = SecurityRelationId; -+ earlySecAttr *es; -+ -+ for (es = earlySecAttrList; es; es = es->next) -+ { -+ if (es->datid == datid && -+ es->relid == relid && -+ es->seckind == seckind && -+ strcmp(es->secattr, secattr) == 0) -+ return es->secid; -+ } -+ /* Not found */ -+ es = MemoryContextAlloc(TopMemoryContext, -+ sizeof(*es) + strlen(secattr)); -+ es->secid = --dummySecid; -+ es->datid = datid; -+ es->relid = relid; -+ es->seckind = seckind; -+ strcpy(es->secattr, secattr); -+ -+ es->next = earlySecAttrList; -+ earlySecAttrList = es; -+ -+ return es->secid; -+ } -+ -+ static char * -+ earlyOutputSecurityAttr(Oid datid, Oid relid, char seckind, Oid secid) -+ { -+ earlySecAttr *es; -+ -+ for (es = earlySecAttrList; es; es = es->next) -+ { -+ if (es->datid == datid && -+ es->relid == relid && -+ es->seckind == seckind && -+ es->secid == secid) -+ return pstrdup(es->secattr); -+ } -+ return NULL; /* Not found */ -+ } -+ -+ void -+ securityPostBootstrapingMode(void) -+ { -+ Relation rel; -+ HeapTuple tuple; -+ earlySecAttr *es; -+ Datum values[Natts_pg_security]; -+ bool nulls[Natts_pg_security]; -+ -+ if (!earlySecAttrList) -+ return; /* do nothing */ -+ -+ StartTransactionCommand(); -+ -+ /* flush all the cached entries */ -+ rel = heap_open(SecurityRelationId, RowExclusiveLock); -+ for (es = earlySecAttrList; es; es = es->next) -+ { -+ memset(nulls, false, sizeof(nulls)); -+ values[Anum_pg_security_secid - 1] = ObjectIdGetDatum(es->secid); -+ values[Anum_pg_security_datid - 1] = ObjectIdGetDatum(es->datid); -+ values[Anum_pg_security_relid - 1] = ObjectIdGetDatum(es->relid); -+ values[Anum_pg_security_seckind - 1] = CharGetDatum(es->seckind); -+ values[Anum_pg_security_secattr - 1] = CStringGetTextDatum(es->secattr); -+ -+ tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); -+ -+ simple_heap_insert(rel, tuple); -+ CatalogUpdateIndexes(rel, tuple); -+ heap_freetuple(tuple); -+ } -+ heap_close(rel, RowExclusiveLock); -+ -+ CommitTransactionCommand(); -+ } -+ -+ /* -+ * securityOnCreateDatabase -+ * copies all the entries refered by source database -+ */ -+ void -+ securityOnCreateDatabase(Oid src_datid, Oid dst_datid) -+ { -+ Relation rel; -+ ScanKeyData keys[1]; -+ SysScanDesc scan; -+ HeapTuple oldtup, newtup; -+ Datum values[Natts_pg_security]; -+ bool nulls[Natts_pg_security]; -+ bool replaces[Natts_pg_security]; -+ -+ /* Scan all entries with pg_security.datid = src_datid */ -+ ScanKeyInit(&keys[0], -+ Anum_pg_security_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(src_datid)); -+ -+ rel = heap_open(SecurityRelationId, RowExclusiveLock); -+ -+ scan = systable_beginscan(rel, SecuritySecidIndexId, true, -+ SnapshotNow, 1, keys); -+ -+ /* pg_security.datid shall be replaced */ -+ memset(values, 0, sizeof(values)); -+ memset(nulls, false, sizeof(nulls)); -+ memset(replaces, false, sizeof(replaces)); -+ -+ values[Anum_pg_security_datid - 1] = ObjectIdGetDatum(dst_datid); -+ replaces[Anum_pg_security_datid - 1] = true; -+ -+ while (HeapTupleIsValid(oldtup = systable_getnext(scan))) -+ { -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), -+ values, nulls, replaces); -+ simple_heap_insert(rel, newtup); -+ -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ } -+ systable_endscan(scan); -+ -+ heap_close(rel, RowExclusiveLock); -+ } -+ -+ /* -+ * securityOnDropDatabase -+ * drops all the entries refered by dropped database -+ */ -+ void -+ securityOnDropDatabase(Oid datid) -+ { -+ Relation rel; -+ ScanKeyData keys[1]; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ -+ /* Scan all entries with pg_security.datid = datid */ -+ ScanKeyInit(&keys[0], -+ Anum_pg_security_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(datid)); -+ -+ rel = heap_open(SecurityRelationId, RowExclusiveLock); -+ -+ scan = systable_beginscan(rel, SecuritySecidIndexId, true, -+ SnapshotNow, 1, keys); -+ -+ while (HeapTupleIsValid(tuple = systable_getnext(scan))) -+ { -+ simple_heap_delete(rel, &tuple->t_self); -+ } -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, RowExclusiveLock); -+ } -+ -+ /* -+ * InputSecurityAttr -+ */ -+ static Oid -+ InputSecurityAttr(Oid relid, char seckind, const char *secattr) -+ { -+ Relation rel; -+ HeapTuple tuple; -+ Oid datid; -+ Oid secid; -+ Datum values[Natts_pg_security]; -+ bool nulls[Natts_pg_security]; -+ -+ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); -+ -+ if (IsBootstrapProcessingMode()) -+ return earlyInputSecurityAttr(datid, relid, seckind, secattr); -+ -+ /* -+ * Lookup the syscache first -+ */ -+ tuple = SearchSysCache(SECURITYATTR, -+ ObjectIdGetDatum(datid), -+ ObjectIdGetDatum(relid), -+ CharGetDatum(seckind), -+ CStringGetTextDatum(secattr)); -+ if (HeapTupleIsValid(tuple)) -+ { -+ secid = ((Form_pg_security) GETSTRUCT(tuple))->secid; -+ -+ ReleaseSysCache(tuple); -+ -+ return secid; -+ } -+ -+ /* -+ * Insert a new tuple, if not exist -+ */ -+ rel = heap_open(SecurityRelationId, RowExclusiveLock); -+ -+ memset(nulls, false, sizeof(nulls)); -+ secid = GetNewOidWithIndex(rel, SecuritySecidIndexId, -+ Anum_pg_security_secid); -+ values[Anum_pg_security_secid - 1] = ObjectIdGetDatum(secid); -+ values[Anum_pg_security_datid - 1] = ObjectIdGetDatum(datid); -+ values[Anum_pg_security_relid - 1] = ObjectIdGetDatum(relid); -+ values[Anum_pg_security_seckind - 1] = CharGetDatum(seckind); -+ values[Anum_pg_security_secattr - 1] = CStringGetTextDatum(secattr); -+ -+ tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); -+ -+ simple_heap_insert(rel, tuple); -+ CatalogUpdateIndexes(rel, tuple); -+ -+ heap_close(rel, RowExclusiveLock); -+ -+ /* -+ * Newly inserted security label needs to be visible by -+ * later operations in this transaction. -+ */ -+ CommandCounterIncrement(); -+ -+ return secid; -+ } -+ -+ static char * -+ OutputSecurityAttr(Oid relid, char seckind, Oid secid) -+ { -+ Form_pg_security secForm; -+ Oid datid; -+ HeapTuple tuple; -+ Datum datum; -+ bool isnull; -+ char *result; -+ -+ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); -+ -+ if (IsBootstrapProcessingMode()) -+ return earlyOutputSecurityAttr(datid, relid, seckind, secid); -+ -+ tuple = SearchSysCache(SECURITYSECID, -+ ObjectIdGetDatum(secid), -+ ObjectIdGetDatum(datid), -+ 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ return NULL; -+ -+ /* -+ * Integrity checks -+ */ -+ secForm = (Form_pg_security) GETSTRUCT(tuple); -+ if (secForm->relid != relid) -+ goto error; -+ if (secForm->seckind != seckind) -+ goto error; -+ datum = SysCacheGetAttr(SECURITYSECID, tuple, -+ Anum_pg_security_secattr, -+ &isnull); -+ if (isnull) -+ goto error; -+ -+ result = TextDatumGetCString(datum); -+ -+ ReleaseSysCache(tuple); -+ -+ return result; -+ -+ error: -+ ReleaseSysCache(tuple); -+ -+ elog(NOTICE, -+ "invalid pg_security (secid=%u, datid=%u, relid=%u, seckind=%c)" -+ " for relid=%u, seckind=%c secid=%u", -+ secForm->secid, secForm->datid, secForm->relid, secForm->seckind, -+ relid, seckind, secid); -+ -+ return NULL; -+ } -+ -+ /* -+ * input/output handler -+ */ -+ Oid -+ securityRawSecLabelIn(Oid relid, char *seclabel) -+ { -+ seclabel = sepgsqlRawSecLabelIn(seclabel); -+ -+ return InputSecurityAttr(relid, SECKIND_SECURITY_LABEL, seclabel); -+ } -+ -+ char * -+ securityRawSecLabelOut(Oid relid, Oid secid) -+ { -+ char *seclabel = OutputSecurityAttr(relid, SECKIND_SECURITY_LABEL, secid); -+ -+ return sepgsqlRawSecLabelOut(seclabel); -+ } -+ -+ Oid -+ securityTransSecLabelIn(Oid relid, char *seclabel) -+ { -+ seclabel = sepgsqlTransSecLabelIn(seclabel); -+ -+ return securityRawSecLabelIn(relid, seclabel); -+ } -+ -+ char * -+ securityTransSecLabelOut(Oid relid, Oid secid) -+ { -+ char *seclabel = securityRawSecLabelOut(relid, secid); -+ -+ return sepgsqlTransSecLabelOut(seclabel); -+ } -+ -+ /* -+ * Output handler for system columns -+ */ -+ Datum -+ securitySysattSecLabelOut(Oid relid, HeapTuple tuple) -+ { -+ char *seclabel; -+ -+ seclabel = sepgsqlSysattSecLabelOut(relid, tuple); -+ if (!seclabel) -+ seclabel = "unlabled"; -+ -+ return CStringGetTextDatum(seclabel); -+ } -+ -+ /* -+ * securityReclaimOnDropTable -+ * drop orphan entries within pg_security on drop table -+ */ -+ void -+ securityReclaimOnDropTable(Oid relid) -+ { -+ Relation rel; -+ SysScanDesc scan; -+ ScanKeyData key[2]; -+ HeapTuple tuple; -+ Oid database_oid; -+ -+ database_oid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); -+ ScanKeyInit(&key[0], -+ Anum_pg_security_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(database_oid)); -+ ScanKeyInit(&key[1], -+ Anum_pg_security_relid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(relid)); -+ -+ rel = heap_open(SecurityRelationId, RowExclusiveLock); -+ scan = systable_beginscan(rel, SecuritySecattrIndexId, true, -+ SnapshotNow, 2, key); -+ while (HeapTupleIsValid(tuple = systable_getnext(scan))) -+ simple_heap_delete(rel, &tuple->t_self); -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, RowExclusiveLock); -+ } -+ -+ /* -+ * security_quote_relation -+ * returns palloc'de identifier with explicit namespace -+ */ -+ static char * -+ security_quote_relation(Oid relid) -+ { -+ Oid nspoid = get_rel_namespace(relid); -+ char *nspname; -+ char *relname; -+ -+ nspname = get_namespace_name(nspoid); -+ relname = get_rel_name(relid); -+ -+ return quote_qualified_identifier(nspname, relname); -+ } -+ -+ /* -+ * security_reclaim_table -+ * reclaims orphan entries associated to a certain table -+ */ -+ static int -+ security_reclaim_table(Oid relid, char seckind) -+ { -+ StringInfoData query; -+ SPIPlanPtr plan; -+ Oid types[2]; -+ Datum values[2]; -+ Oid proc_oid; -+ Oid database_oid; -+ char *relname_full; -+ char *attname_datid; -+ char *attname_relid; -+ char *attname_secid; -+ char *attname_seckind; -+ char *attname_secattr; -+ char *sec_proname; -+ char *sec_nspname; -+ Form_pg_proc proForm; -+ HeapTuple protup; -+ -+ /* -+ * LOCK the target table -+ */ -+ initStringInfo(&query); -+ relname_full = security_quote_relation(relid); -+ appendStringInfo(&query, "LOCK %s IN SHARE MODE", relname_full); -+ if (SPI_execute(query.data, false, 0) != SPI_OK_UTILITY) -+ elog(ERROR, "SPI_execute failed on %s", query.data); -+ -+ /* -+ * DELETE orphan entries -+ */ -+ initStringInfo(&query); -+ attname_secid = get_attname(SecurityRelationId, Anum_pg_security_secid); -+ attname_datid = get_attname(SecurityRelationId, Anum_pg_security_datid); -+ attname_relid = get_attname(SecurityRelationId, Anum_pg_security_relid); -+ attname_seckind = get_attname(SecurityRelationId, Anum_pg_security_seckind); -+ attname_secattr = get_attname(SecurityRelationId, Anum_pg_security_secattr); -+ -+ appendStringInfo(&query, -+ "DELETE FROM %s " -+ "WHERE %s = $1 AND %s = $2 " -+ " AND %s = $3 AND %s NOT IN ", -+ security_quote_relation(SecurityRelationId), -+ quote_identifier(attname_datid), -+ quote_identifier(attname_relid), -+ quote_identifier(attname_seckind), -+ quote_identifier(attname_secid)); -+ switch (seckind) -+ { -+ case SECKIND_SECURITY_LABEL: -+ proc_oid = F_SECURITY_LABEL_TO_SECID; -+ break; -+ default: -+ elog(ERROR, "unexpected seckind: %c", seckind); -+ proc_oid = InvalidOid; /* to compiler silent */ -+ break; -+ } -+ -+ protup = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(proc_oid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(protup)) -+ elog(ERROR, "cache lookup failed for procedure: %u", proc_oid); -+ proForm = (Form_pg_proc) GETSTRUCT(protup); -+ sec_proname = NameStr(proForm->proname); -+ sec_nspname = get_namespace_name(proForm->pronamespace); -+ -+ appendStringInfo(&query, -+ "(SELECT %s.%s(%s) FROM ONLY %s)", -+ quote_identifier(sec_nspname), -+ quote_identifier(sec_proname), -+ quote_identifier(get_rel_name(relid)), -+ relname_full); -+ ReleaseSysCache(protup); -+ -+ /* -+ * Setup and execute query -+ */ -+ types[0] = OIDOID; -+ types[1] = OIDOID; -+ types[2] = CHAROID; -+ plan = SPI_prepare(query.data, 3, types); -+ if (!plan) -+ elog(ERROR, "SPI_prepare failed on %s", query.data); -+ -+ database_oid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); -+ -+ values[0] = ObjectIdGetDatum(database_oid); -+ values[1] = ObjectIdGetDatum(relid); -+ values[2] = CharGetDatum(seckind); -+ if (SPI_execute_plan(plan, values, NULL, false, 0) != SPI_OK_DELETE) -+ elog(ERROR, "SPI_execute_plan failed on %s", query.data); -+ -+ SPI_freetuptable(SPI_tuptable); -+ -+ return SPI_processed; -+ } -+ -+ static int -+ security_reclaim_all_tables(char seckind) -+ { -+ StringInfoData query; -+ char *attname_datid; -+ char *attname_relid; -+ int index; -+ Datum datum; -+ bool isnull; -+ int count = 0; -+ List *relidList = NIL; -+ ListCell *l; -+ -+ initStringInfo(&query); -+ -+ attname_datid = get_attname(SecurityRelationId, Anum_pg_security_datid); -+ attname_relid = get_attname(SecurityRelationId, Anum_pg_security_relid); -+ appendStringInfo(&query, -+ "SELECT DISTINCT %s FROM %s WHERE %s IN (%u,%u)", -+ attname_relid, -+ security_quote_relation(SecurityRelationId), -+ attname_datid, InvalidOid, MyDatabaseId); -+ -+ if (SPI_execute(query.data, true, 0) != SPI_OK_SELECT) -+ elog(ERROR, "SPI_execute failed on %s", query.data); -+ -+ for (index = 0; index < SPI_processed; index++) -+ { -+ datum = SPI_getbinval(SPI_tuptable->vals[index], -+ SPI_tuptable->tupdesc, 1, &isnull); -+ if (isnull) -+ continue; -+ relidList = lappend_oid(relidList, DatumGetObjectId(datum)); -+ } -+ SPI_freetuptable(SPI_tuptable); -+ -+ foreach (l, relidList) -+ count += security_reclaim_table(lfirst_oid(l), seckind); -+ -+ return count; -+ } -+ -+ static int -+ security_reclaim(Oid relid, char seckind) -+ { -+ int saved_mode; -+ int count; -+ -+ if (!superuser()) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("must be superuser to reclaim security attributes"))); -+ /* -+ * Disables SE-PostgreSQL temporary -+ */ -+ saved_mode = sepgsqlSetEnforce(0); -+ -+ PG_TRY(); -+ { -+ if (SPI_connect() != SPI_OK_CONNECT) -+ elog(ERROR, "SPI_connect failed"); -+ -+ if (OidIsValid(relid)) -+ count = security_reclaim_table(relid, seckind); -+ else -+ count = security_reclaim_all_tables(seckind); -+ -+ if (SPI_finish() != SPI_OK_FINISH) -+ elog(ERROR, "SPI_finish failed"); -+ } -+ PG_CATCH(); -+ { -+ sepgsqlSetEnforce(saved_mode); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ sepgsqlSetEnforce(saved_mode); -+ -+ return count; -+ } -+ -+ Datum -+ security_reclaim_label(PG_FUNCTION_ARGS) -+ { -+ PG_RETURN_INT32(security_reclaim(InvalidOid, SECKIND_SECURITY_LABEL)); -+ } -+ -+ Datum -+ security_reclaim_table_label(PG_FUNCTION_ARGS) -+ { -+ PG_RETURN_INT32(security_reclaim(PG_GETARG_OID(0), SECKIND_SECURITY_LABEL)); -+ } -+ -+ Datum -+ security_label_to_secid(PG_FUNCTION_ARGS) -+ { -+ HeapTupleHeader tuphdr = PG_GETARG_HEAPTUPLEHEADER(0); -+ -+ PG_RETURN_OID(HeapTupleHeaderGetSecid(tuphdr)); -+ } -diff -Nrpc base/src/backend/catalog/pg_shdepend.c sepgsql/src/backend/catalog/pg_shdepend.c -*** base/src/backend/catalog/pg_shdepend.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/pg_shdepend.c Fri Sep 18 14:51:00 2009 -*************** -*** 36,41 **** ---- 36,42 ---- - #include "commands/schemacmds.h" - #include "commands/tablecmds.h" - #include "commands/typecmds.h" -+ #include "security/sepgsql.h" - #include "storage/lmgr.h" - #include "miscadmin.h" - #include "utils/acl.h" -*************** shdepReassignOwned(List *roleids, Oid ne -*** 1336,1341 **** ---- 1337,1344 ---- - break; - - case TypeRelationId: -+ /* SELinux checks */ -+ sepgsql_type_alter(sdepForm->objid, NULL, InvalidOid); - AlterTypeOwnerInternal(sdepForm->objid, newrole, true); - break; - -*************** shdepReassignOwned(List *roleids, Oid ne -*** 1348,1354 **** - break; - - case RelationRelationId: -! - /* - * Pass recursing = true so that we don't fail on indexes, - * owned sequences, etc when we happen to visit them ---- 1351,1358 ---- - break; - - case RelationRelationId: -! /* SELinux checks */ -! sepgsql_relation_alter(sdepForm->objid, NULL, InvalidOid); - /* - * Pass recursing = true so that we don't fail on indexes, - * owned sequences, etc when we happen to visit them -diff -Nrpc base/src/backend/catalog/pg_type.c sepgsql/src/backend/catalog/pg_type.c -*** base/src/backend/catalog/pg_type.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/catalog/pg_type.c Fri Sep 18 17:39:46 2009 -*************** -*** 25,30 **** ---- 25,31 ---- - #include "commands/typecmds.h" - #include "miscadmin.h" - #include "parser/scansup.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -*************** TypeShellMake(const char *typeName, Oid -*** 56,65 **** ---- 57,73 ---- - Datum values[Natts_pg_type]; - bool nulls[Natts_pg_type]; - Oid typoid; -+ Oid typsid; - NameData name; - - Assert(PointerIsValid(typeName)); - -+ /* SELinux check permission to create a shell type */ -+ typsid = sepgsql_type_create(typeName, InvalidOid, typeNamespace, -+ F_SHELL_IN, F_SHELL_OUT, -+ InvalidOid, InvalidOid, -+ InvalidOid, InvalidOid, InvalidOid); -+ - /* - * open pg_type - */ -*************** TypeCreate(Oid newTypeOid, -*** 201,206 **** ---- 209,215 ---- - { - Relation pg_type_desc; - Oid typeObjectId; -+ Oid typeSecid = InvalidOid; - bool rebuildDeps = false; - HeapTuple tup; - bool nulls[Natts_pg_type]; -*************** TypeCreate(Oid newTypeOid, -*** 367,372 **** ---- 376,390 ---- - CStringGetDatum(typeName), - ObjectIdGetDatum(typeNamespace), - 0, 0); -+ -+ /* SELinux checks to create/replace type */ -+ if (!isImplicitArray && typeType != TYPTYPE_COMPOSITE) -+ typeSecid = sepgsql_type_create(typeName, tup, typeNamespace, -+ inputProcedure, outputProcedure, -+ receiveProcedure, sendProcedure, -+ typmodinProcedure, typmodoutProcedure, -+ analyzeProcedure); -+ - if (HeapTupleIsValid(tup)) - { - /* -*************** TypeCreate(Oid newTypeOid, -*** 412,417 **** ---- 430,437 ---- - /* Force the OID if requested by caller, else heap_insert does it */ - if (OidIsValid(newTypeOid)) - HeapTupleSetOid(tup, newTypeOid); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, typeSecid); - - typeObjectId = simple_heap_insert(pg_type_desc, tup); - } -diff -Nrpc base/src/backend/catalog/toasting.c sepgsql/src/backend/catalog/toasting.c -*** base/src/backend/catalog/toasting.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/catalog/toasting.c Thu Oct 8 09:29:32 2009 -*************** -*** 28,33 **** ---- 28,34 ---- - #include "catalog/toasting.h" - #include "miscadmin.h" - #include "nodes/makefuncs.h" -+ #include "security/sepgsql.h" - #include "utils/builtins.h" - #include "utils/syscache.h" - -*************** create_toast_table(Relation rel, Oid toa -*** 125,130 **** ---- 126,132 ---- - char toast_relname[NAMEDATALEN]; - char toast_idxname[NAMEDATALEN]; - IndexInfo *indexInfo; -+ Oid *secLabels; - Oid classObjectId[2]; - int16 coloptions[2]; - ObjectAddress baseobject, -*************** create_toast_table(Relation rel, Oid toa -*** 199,204 **** ---- 201,211 ---- - else - namespaceid = PG_TOAST_NAMESPACE; - -+ secLabels = sepgsql_relation_create(toast_relname, -+ RELKIND_TOASTVALUE, -+ tupdesc, namespaceid, -+ NULL, NIL, false, false); -+ - toast_relid = heap_create_with_catalog(toast_relname, - namespaceid, - rel->rd_rel->reltablespace, -*************** create_toast_table(Relation rel, Oid toa -*** 212,218 **** - 0, - ONCOMMIT_NOOP, - reloptions, -! true); - - /* make the toast relation visible, else index creation will fail */ - CommandCounterIncrement(); ---- 219,226 ---- - 0, - ONCOMMIT_NOOP, - reloptions, -! true, -! secLabels); - - /* make the toast relation visible, else index creation will fail */ - CommandCounterIncrement(); -diff -Nrpc base/src/backend/commands/aggregatecmds.c sepgsql/src/backend/commands/aggregatecmds.c -*** base/src/backend/commands/aggregatecmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/aggregatecmds.c Thu Sep 17 22:10:19 2009 -*************** -*** 32,37 **** ---- 32,38 ---- - #include "miscadmin.h" - #include "parser/parse_func.h" - #include "parser/parse_type.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -*************** RenameAggregate(List *name, List *args, -*** 311,316 **** ---- 312,320 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux permission checks */ -+ sepgsql_proc_alter(procOid, newname, InvalidOid); -+ - /* rename */ - namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname); - simple_heap_update(rel, &tup->t_self, tup); -diff -Nrpc base/src/backend/commands/alter.c sepgsql/src/backend/commands/alter.c -*** base/src/backend/commands/alter.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/commands/alter.c Fri Sep 18 14:51:00 2009 -*************** ExecAlterOwnerStmt(AlterOwnerStmt *stmt) -*** 284,286 **** ---- 284,315 ---- - (int) stmt->objectType); - } - } -+ -+ void -+ ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt) -+ { -+ DefElem *seclabel = (DefElem *)stmt->secLabel; -+ -+ switch (stmt->objectType) -+ { -+ case OBJECT_DATABASE: -+ AlterDatabaseSecLabel(strVal(linitial(stmt->object)), seclabel); -+ break; -+ case OBJECT_SCHEMA: -+ AlterSchemaSecLabel(strVal(linitial(stmt->object)), seclabel); -+ break; -+ case OBJECT_TABLE: -+ case OBJECT_SEQUENCE: -+ case OBJECT_COLUMN: -+ CheckRelationOwnership(stmt->relation, true); -+ AlterRelationSecLabel(stmt->relation, stmt->subname, -+ stmt->objectType, seclabel); -+ break; -+ case OBJECT_FUNCTION: -+ AlterFunctionSecLabel(stmt->object, stmt->objarg, seclabel); -+ break; -+ default: -+ elog(ERROR, "unrecognized AlterSecLabelStmt type: %d", -+ (int) stmt->objectType); -+ } -+ } -diff -Nrpc base/src/backend/commands/cluster.c sepgsql/src/backend/commands/cluster.c -*** base/src/backend/commands/cluster.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/cluster.c Fri Sep 18 14:51:00 2009 -*************** -*** 36,41 **** ---- 36,42 ---- - #include "commands/trigger.h" - #include "commands/vacuum.h" - #include "miscadmin.h" -+ #include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/procarray.h" - #include "utils/acl.h" -*************** rebuild_relation(Relation OldHeap, Oid i -*** 617,624 **** - /* - * The new relation is local to our transaction and we know nothing - * depends on it, so DROP_RESTRICT should be OK. - */ -! performDeletion(&object, DROP_RESTRICT); - - /* performDeletion does CommandCounterIncrement at end */ - ---- 618,626 ---- - /* - * The new relation is local to our transaction and we know nothing - * depends on it, so DROP_RESTRICT should be OK. -+ * SELinux does not check any permissions here. - */ -! performDeletionNoPerms(&object, DROP_RESTRICT); - - /* performDeletion does CommandCounterIncrement at end */ - -*************** make_new_heap(Oid OIDOldHeap, const char -*** 712,718 **** - 0, - ONCOMMIT_NOOP, - reloptions, -! allowSystemTableMods); - - ReleaseSysCache(tuple); - ---- 714,721 ---- - 0, - ONCOMMIT_NOOP, - reloptions, -! allowSystemTableMods, -! sepgsql_relation_copy(OldHeap)); - - ReleaseSysCache(tuple); - -*************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl -*** 924,929 **** ---- 927,936 ---- - if (NewHeap->rd_rel->relhasoids) - HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); - -+ /* Preserve SID, if any */ -+ if (HeapTupleHasSecid(copiedTuple)) -+ HeapTupleSetSecid(copiedTuple, HeapTupleGetSecid(tuple)); -+ - /* The heap rewrite module does the rest */ - rewrite_heap_tuple(rwstate, tuple, copiedTuple); - -diff -Nrpc base/src/backend/commands/conversioncmds.c sepgsql/src/backend/commands/conversioncmds.c -*** base/src/backend/commands/conversioncmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/conversioncmds.c Thu Sep 17 22:10:19 2009 -*************** -*** 24,29 **** ---- 24,30 ---- - #include "mb/pg_wchar.h" - #include "miscadmin.h" - #include "parser/parse_func.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -*************** CreateConversionCommand(CreateConversion -*** 45,50 **** ---- 46,52 ---- - int from_encoding; - int to_encoding; - Oid funcoid; -+ Oid secid; - const char *from_encoding_name = stmt->for_encoding_name; - const char *to_encoding_name = stmt->to_encoding_name; - List *func_name = stmt->func_name; -*************** CreateConversionCommand(CreateConversion -*** 96,101 **** ---- 98,106 ---- - aclcheck_error(aclresult, ACL_KIND_PROC, - NameListToString(func_name)); - -+ /* SELinux checks */ -+ secid = sepgsql_conversion_create(conversion_name, namespaceId, funcoid); -+ - /* - * Check that the conversion function is suitable for the requested source - * and target encodings. We do that by calling the function with an empty -*************** CreateConversionCommand(CreateConversion -*** 114,120 **** - * name) - */ - ConversionCreate(conversion_name, namespaceId, GetUserId(), -! from_encoding, to_encoding, funcoid, stmt->def); - } - - /* ---- 119,125 ---- - * name) - */ - ConversionCreate(conversion_name, namespaceId, GetUserId(), -! from_encoding, to_encoding, funcoid, secid, stmt->def); - } - - /* -*************** RenameConversion(List *name, const char -*** 240,245 **** ---- 245,253 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux checks */ -+ sepgsql_conversion_alter(conversionOid, newname); -+ - /* rename */ - namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); - simple_heap_update(rel, &tup->t_self, tup); -*************** AlterConversionOwner_internal(Relation r -*** 336,341 **** ---- 344,351 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(convForm->connamespace)); - } -+ /* SELinux checks */ -+ sepgsql_conversion_alter(HeapTupleGetOid(tup), NULL); - - /* - * Modify the owner --- okay to scribble on tup because it's a copy -diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c -*** base/src/backend/commands/copy.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/copy.c Mon Sep 28 09:29:32 2009 -*************** -*** 21,28 **** ---- 21,31 ---- - #include - - #include "access/heapam.h" -+ #include "access/sysattr.h" - #include "access/xact.h" -+ #include "catalog/heap.h" - #include "catalog/namespace.h" -+ #include "catalog/pg_security.h" - #include "catalog/pg_type.h" - #include "commands/copy.h" - #include "commands/trigger.h" -*************** -*** 34,39 **** ---- 37,44 ---- - #include "optimizer/planner.h" - #include "parser/parse_relation.h" - #include "rewrite/rewriteHandler.h" -+ #include "security/rowlevel.h" -+ #include "security/sepgsql.h" - #include "storage/fd.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" -*************** typedef struct CopyStateData -*** 160,165 **** ---- 165,174 ---- - char *raw_buf; - int raw_buf_index; /* next byte to process */ - int raw_buf_len; /* total # of bytes stored */ -+ -+ /* dump/restore support for security_label */ -+ FmgrInfo seclabel_out_function; -+ bool seclabel_force_quot; - } CopyStateData; - - typedef CopyStateData *CopyState; -*************** static const char BinarySignature[11] = -*** 243,250 **** - /* non-export function prototypes */ - static void DoCopyTo(CopyState cstate); - static void CopyTo(CopyState cstate); -! static void CopyOneRowTo(CopyState cstate, Oid tupleOid, -! Datum *values, bool *nulls); - static void CopyFrom(CopyState cstate); - static bool CopyReadLine(CopyState cstate); - static bool CopyReadLineText(CopyState cstate); ---- 252,259 ---- - /* non-export function prototypes */ - static void DoCopyTo(CopyState cstate); - static void CopyTo(CopyState cstate); -! static void CopyOneRowTo(CopyState cstate, HeapTuple tuple, -! Datum *values, bool *nulls); - static void CopyFrom(CopyState cstate); - static bool CopyReadLine(CopyState cstate); - static bool CopyReadLineText(CopyState cstate); -*************** DoCopy(const CopyStmt *stmt, const char -*** 958,969 **** - errmsg("CSV quote character must not appear in the NULL specification"))); - - /* Disallow file COPY except to superusers. */ -! if (!pipe && !superuser()) -! ereport(ERROR, -! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -! errmsg("must be superuser to COPY to or from a file"), -! errhint("Anyone can COPY to stdout or from stdin. " -! "psql's \\copy command also works for anyone."))); - - if (stmt->relation) - { ---- 967,985 ---- - errmsg("CSV quote character must not appear in the NULL specification"))); - - /* Disallow file COPY except to superusers. */ -! if (!pipe) -! { -! if (!superuser()) -! ereport(ERROR, -! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -! errmsg("must be superuser to COPY to or from a file"), -! errhint("Anyone can COPY to stdout or from stdin. " -! "psql's \\copy command also works for anyone."))); -! if (is_from) -! sepgsql_file_read(stmt->filename); -! else -! sepgsql_file_write(stmt->filename); -! } - - if (stmt->relation) - { -*************** DoCopy(const CopyStmt *stmt, const char -*** 1090,1095 **** ---- 1106,1114 ---- - - num_phys_attrs = tupDesc->natts; - -+ /* SELinux: check table/column level permission */ -+ sepgsqlCheckCopyTable(cstate->rel, cstate->attnumlist, is_from); -+ - /* Convert FORCE QUOTE name list to per-column flags, check validity */ - cstate->force_quote_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool)); - if (force_quote) -*************** DoCopy(const CopyStmt *stmt, const char -*** 1104,1114 **** - int attnum = lfirst_int(cur); - - if (!list_member_int(cstate->attnumlist, attnum)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", -! NameStr(tupDesc->attrs[attnum - 1]->attname)))); -! cstate->force_quote_flags[attnum - 1] = true; - } - } - ---- 1123,1153 ---- - int attnum = lfirst_int(cur); - - if (!list_member_int(cstate->attnumlist, attnum)) -+ { -+ Form_pg_attribute attForm; -+ -+ if (SystemAttributeIsWritable(attnum)) -+ attForm = SystemAttributeDefinition(attnum, true); -+ else -+ attForm = tupDesc->attrs[attnum - 1]; -+ -+ Assert(attForm != NULL); -+ - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", -! NameStr(attForm->attname)))); -! } -! -! switch (attnum) -! { -! case SecurityAttributeNumber: -! cstate->seclabel_force_quot = true; -! break; -! default: -! cstate->force_quote_flags[attnum - 1] = true; -! break; -! } - } - } - -*************** DoCopy(const CopyStmt *stmt, const char -*** 1126,1135 **** - int attnum = lfirst_int(cur); - - if (!list_member_int(cstate->attnumlist, attnum)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", -! NameStr(tupDesc->attrs[attnum - 1]->attname)))); - cstate->force_notnull_flags[attnum - 1] = true; - } - } ---- 1165,1187 ---- - int attnum = lfirst_int(cur); - - if (!list_member_int(cstate->attnumlist, attnum)) -+ { -+ Form_pg_attribute attForm; -+ -+ if (SystemAttributeIsWritable(attnum)) -+ attForm = SystemAttributeDefinition(attnum, true); -+ else -+ attForm = tupDesc->attrs[attnum - 1]; -+ -+ Assert(attForm != NULL); -+ - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", -! NameStr(attForm->attname)))); -! } -! if (SystemAttributeIsWritable(attnum)) -! continue; /* ignore, if specified */ - cstate->force_notnull_flags[attnum - 1] = true; - } - } -*************** CopyTo(CopyState cstate) -*** 1321,1336 **** - int attnum = lfirst_int(cur); - Oid out_func_oid; - bool isvarlena; - - if (cstate->binary) -! getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, - &out_func_oid, - &isvarlena); - else -! getTypeOutputInfo(attr[attnum - 1]->atttypid, - &out_func_oid, - &isvarlena); -! fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); - } - - /* ---- 1373,1403 ---- - int attnum = lfirst_int(cur); - Oid out_func_oid; - bool isvarlena; -+ FmgrInfo *out_fmgr; -+ Form_pg_attribute attForm; -+ -+ switch (attnum) -+ { -+ case SecurityAttributeNumber: -+ attForm = SystemAttributeDefinition(attnum, true); -+ out_fmgr = &cstate->seclabel_out_function; -+ break; -+ -+ default: -+ attForm = attr[attnum - 1]; -+ out_fmgr = &cstate->out_functions[attnum - 1]; -+ break; -+ } - - if (cstate->binary) -! getTypeBinaryOutputInfo(attForm->atttypid, - &out_func_oid, - &isvarlena); - else -! getTypeOutputInfo(attForm->atttypid, - &out_func_oid, - &isvarlena); -! fmgr_info(out_func_oid, out_fmgr); - } - - /* -*************** CopyTo(CopyState cstate) -*** 1385,1391 **** - CopySendChar(cstate, cstate->delim[0]); - hdr_delim = true; - -! colname = NameStr(attr[attnum - 1]->attname); - - CopyAttributeOutCSV(cstate, colname, false, - list_length(cstate->attnumlist) == 1); ---- 1452,1465 ---- - CopySendChar(cstate, cstate->delim[0]); - hdr_delim = true; - -! if (SystemAttributeIsWritable(attnum)) -! { -! Form_pg_attribute attForm -! = SystemAttributeDefinition(attnum, true); -! colname = NameStr(attForm->attname); -! } -! else -! colname = NameStr(attr[attnum - 1]->attname); - - CopyAttributeOutCSV(cstate, colname, false, - list_length(cstate->attnumlist) == 1); -*************** CopyTo(CopyState cstate) -*** 1411,1421 **** - { - CHECK_FOR_INTERRUPTS(); - - /* Deconstruct the tuple ... faster than repeated heap_getattr */ - heap_deform_tuple(tuple, tupDesc, values, nulls); - - /* Format and send the data */ -! CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls); - } - - heap_endscan(scandesc); ---- 1485,1499 ---- - { - CHECK_FOR_INTERRUPTS(); - -+ /* check Row-level permission on the tuple */ -+ if (!rowlvCopyToTuple(cstate->rel, tuple)) -+ continue; -+ - /* Deconstruct the tuple ... faster than repeated heap_getattr */ - heap_deform_tuple(tuple, tupDesc, values, nulls); - - /* Format and send the data */ -! CopyOneRowTo(cstate, tuple, values, nulls); - } - - heap_endscan(scandesc); -*************** CopyTo(CopyState cstate) -*** 1441,1447 **** - * Emit one row during CopyTo(). - */ - static void -! CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) - { - bool need_delim = false; - FmgrInfo *out_functions = cstate->out_functions; ---- 1519,1526 ---- - * Emit one row during CopyTo(). - */ - static void -! CopyOneRowTo(CopyState cstate, HeapTuple tuple, -! Datum *values, bool *nulls) - { - bool need_delim = false; - FmgrInfo *out_functions = cstate->out_functions; -*************** CopyOneRowTo(CopyState cstate, Oid tuple -*** 1461,1467 **** - { - /* Hack --- assume Oid is same size as int32 */ - CopySendInt32(cstate, sizeof(int32)); -! CopySendInt32(cstate, tupleOid); - } - } - else ---- 1540,1546 ---- - { - /* Hack --- assume Oid is same size as int32 */ - CopySendInt32(cstate, sizeof(int32)); -! CopySendInt32(cstate, HeapTupleGetOid(tuple)); - } - } - else -*************** CopyOneRowTo(CopyState cstate, Oid tuple -*** 1471,1477 **** - if (cstate->oids) - { - string = DatumGetCString(DirectFunctionCall1(oidout, -! ObjectIdGetDatum(tupleOid))); - CopySendString(cstate, string); - need_delim = true; - } ---- 1550,1556 ---- - if (cstate->oids) - { - string = DatumGetCString(DirectFunctionCall1(oidout, -! ObjectIdGetDatum(HeapTupleGetOid(tuple)))); - CopySendString(cstate, string); - need_delim = true; - } -*************** CopyOneRowTo(CopyState cstate, Oid tuple -*** 1480,1487 **** - foreach(cur, cstate->attnumlist) - { - int attnum = lfirst_int(cur); -! Datum value = values[attnum - 1]; -! bool isnull = nulls[attnum - 1]; - - if (!cstate->binary) - { ---- 1559,1569 ---- - foreach(cur, cstate->attnumlist) - { - int attnum = lfirst_int(cur); -! Oid relid; -! Datum value; -! bool isnull; -! bool force_quot; -! FmgrInfo *out_fmgr; - - if (!cstate->binary) - { -*************** CopyOneRowTo(CopyState cstate, Oid tuple -*** 1490,1495 **** ---- 1572,1595 ---- - need_delim = true; - } - -+ switch (attnum) -+ { -+ case SecurityAttributeNumber: -+ relid = RelationGetRelid(cstate->rel); -+ value = securitySysattSecLabelOut(relid, tuple); -+ isnull = false; -+ force_quot = cstate->seclabel_force_quot; -+ out_fmgr = &cstate->seclabel_out_function; -+ break; -+ -+ default: -+ value = values[attnum - 1]; -+ isnull = nulls[attnum - 1]; -+ force_quot = cstate->force_quote_flags[attnum - 1]; -+ out_fmgr = &out_functions[attnum - 1]; -+ break; -+ } -+ - if (isnull) - { - if (!cstate->binary) -*************** CopyOneRowTo(CopyState cstate, Oid tuple -*** 1501,1511 **** - { - if (!cstate->binary) - { -! string = OutputFunctionCall(&out_functions[attnum - 1], -! value); - if (cstate->csv_mode) -! CopyAttributeOutCSV(cstate, string, -! cstate->force_quote_flags[attnum - 1], - list_length(cstate->attnumlist) == 1); - else - CopyAttributeOutText(cstate, string); ---- 1601,1609 ---- - { - if (!cstate->binary) - { -! string = OutputFunctionCall(out_fmgr, value); - if (cstate->csv_mode) -! CopyAttributeOutCSV(cstate, string, force_quot, - list_length(cstate->attnumlist) == 1); - else - CopyAttributeOutText(cstate, string); -*************** CopyOneRowTo(CopyState cstate, Oid tuple -*** 1514,1521 **** - { - bytea *outputbytes; - -! outputbytes = SendFunctionCall(&out_functions[attnum - 1], -! value); - CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); - CopySendData(cstate, VARDATA(outputbytes), - VARSIZE(outputbytes) - VARHDRSZ); ---- 1612,1618 ---- - { - bytea *outputbytes; - -! outputbytes = SendFunctionCall(out_fmgr, value); - CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); - CopySendData(cstate, VARDATA(outputbytes), - VARSIZE(outputbytes) - VARHDRSZ); -*************** CopyFrom(CopyState cstate) -*** 1649,1656 **** ---- 1746,1755 ---- - num_defaults; - FmgrInfo *in_functions; - FmgrInfo oid_in_function; -+ FmgrInfo seclabel_in_function; - Oid *typioparams; - Oid oid_typioparam; -+ Oid seclabel_typioparam; - int attnum; - int i; - Oid in_func_oid; -*************** CopyFrom(CopyState cstate) -*** 1888,1893 **** ---- 1987,2004 ---- - fmgr_info(in_func_oid, &oid_in_function); - } - -+ if (list_member_int(cstate->attnumlist, -+ SecurityAttributeNumber)) -+ { -+ if (!cstate->binary) -+ getTypeInputInfo(TEXTOID, -+ &in_func_oid, &seclabel_typioparam); -+ else -+ getTypeBinaryInputInfo(TEXTOID, -+ &in_func_oid, &seclabel_typioparam); -+ fmgr_info(in_func_oid, &seclabel_in_function); -+ } -+ - values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); - nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); - -*************** CopyFrom(CopyState cstate) -*** 1922,1927 **** ---- 2033,2039 ---- - { - bool skip_tuple; - Oid loaded_oid = InvalidOid; -+ Oid loaded_seclabel = InvalidOid; - - CHECK_FOR_INTERRUPTS(); - -*************** CopyFrom(CopyState cstate) -*** 1993,2006 **** - /* Loop to read the user attributes on the line. */ - foreach(cur, cstate->attnumlist) - { - int attnum = lfirst_int(cur); - int m = attnum - 1; - - if (fieldno >= fldct) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("missing data for column \"%s\"", -! NameStr(attr[m]->attname)))); - string = field_strings[fieldno++]; - - if (cstate->csv_mode && string == NULL && ---- 2105,2125 ---- - /* Loop to read the user attributes on the line. */ - foreach(cur, cstate->attnumlist) - { -+ Form_pg_attribute attForm; -+ Datum dat; - int attnum = lfirst_int(cur); - int m = attnum - 1; - -+ if (SystemAttributeIsWritable(attnum)) -+ attForm = SystemAttributeDefinition(attnum, true); -+ else -+ attForm = attr[m]; -+ - if (fieldno >= fldct) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("missing data for column \"%s\"", -! NameStr(attForm->attname)))); - string = field_strings[fieldno++]; - - if (cstate->csv_mode && string == NULL && -*************** CopyFrom(CopyState cstate) -*** 2010,2023 **** - string = cstate->null_print; - } - -! cstate->cur_attname = NameStr(attr[m]->attname); - cstate->cur_attval = string; -! values[m] = InputFunctionCall(&in_functions[m], -! string, -! typioparams[m], -! attr[m]->atttypmod); -! if (string != NULL) -! nulls[m] = false; - cstate->cur_attname = NULL; - cstate->cur_attval = NULL; - } ---- 2129,2168 ---- - string = cstate->null_print; - } - -! cstate->cur_attname = NameStr(attForm->attname); - cstate->cur_attval = string; -! -! switch (attnum) -! { -! case SecurityAttributeNumber: -! if (!string) -! break; -! -! dat = InputFunctionCall(&seclabel_in_function, -! string, -! seclabel_typioparam, -! attForm->atttypmod); -! loaded_seclabel -! = securityTransSecLabelIn(RelationGetRelid(cstate->rel), -! TextDatumGetCString(dat)); -! break; -! -! default: -! if (cstate->csv_mode && string == NULL && -! cstate->force_notnull_flags[m]) -! { -! /* Go ahead and read the NULL string */ -! string = cstate->null_print; -! } -! -! values[m] = InputFunctionCall(&in_functions[m], -! string, -! typioparams[m], -! attForm->atttypmod); -! if (string != NULL) -! nulls[m] = false; -! break; -! } - cstate->cur_attname = NULL; - cstate->cur_attval = NULL; - } -*************** CopyFrom(CopyState cstate) -*** 2063,2079 **** - i = 0; - foreach(cur, cstate->attnumlist) - { - int attnum = lfirst_int(cur); - int m = attnum - 1; - -! cstate->cur_attname = NameStr(attr[m]->attname); - i++; -! values[m] = CopyReadBinaryAttribute(cstate, -! i, -! &in_functions[m], -! typioparams[m], -! attr[m]->atttypmod, -! &nulls[m]); - cstate->cur_attname = NULL; - } - } ---- 2208,2248 ---- - i = 0; - foreach(cur, cstate->attnumlist) - { -+ Form_pg_attribute attForm; -+ Datum dat; - int attnum = lfirst_int(cur); - int m = attnum - 1; - -! if (SystemAttributeIsWritable(attnum)) -! attForm = SystemAttributeDefinition(attnum, false); -! else -! attForm = attr[m]; -! -! cstate->cur_attname = NameStr(attForm->attname); - i++; -! -! switch (attnum) -! { -! case SecurityAttributeNumber: -! dat = CopyReadBinaryAttribute(cstate, i, -! &seclabel_in_function, -! seclabel_typioparam, -! attForm->atttypmod, -! &isnull); -! if (!isnull) -! loaded_seclabel -! = securityTransSecLabelIn(RelationGetRelid(cstate->rel), -! TextDatumGetCString(dat)); -! break; -! -! default: -! values[m] = CopyReadBinaryAttribute(cstate, i, -! &in_functions[m], -! typioparams[m], -! attr[m]->atttypmod, -! &nulls[m]); -! break; -! } - cstate->cur_attname = NULL; - } - } -*************** CopyFrom(CopyState cstate) -*** 2094,2099 **** ---- 2263,2270 ---- - - if (cstate->oids && file_has_oids) - HeapTupleSetOid(tuple, loaded_oid); -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, loaded_seclabel); - - /* Triggers and stuff need to be invoked in query context. */ - MemoryContextSwitchTo(oldcontext); -*************** CopyFrom(CopyState cstate) -*** 2118,2123 **** ---- 2289,2297 ---- - } - - if (!skip_tuple) -+ sepgsqlHeapTupleInsert(cstate->rel, tuple, false); -+ -+ if (!skip_tuple) - { - /* Place tuple in tuple slot */ - ExecStoreTuple(tuple, slot, InvalidBuffer, false); -*************** CopyGetAttnums(TupleDesc tupDesc, Relati -*** 3398,3403 **** ---- 3572,3584 ---- - } - if (attnum == InvalidAttrNumber) - { -+ Form_pg_attribute attForm -+ = SystemAttributeByName(name, tupDesc->tdhasoid); -+ if (attForm && SystemAttributeIsWritable(attForm->attnum)) -+ attnum = attForm->attnum; -+ } -+ if (attnum == InvalidAttrNumber) -+ { - if (rel != NULL) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), -*************** copy_dest_receive(TupleTableSlot *slot, -*** 3445,3451 **** - slot_getallattrs(slot); - - /* And send the data */ -! CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); - } - - /* ---- 3626,3633 ---- - slot_getallattrs(slot); - - /* And send the data */ -! CopyOneRowTo(cstate, slot->tts_tuple, -! slot->tts_values, slot->tts_isnull); - } - - /* -diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/dbcommands.c -*** base/src/backend/commands/dbcommands.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/dbcommands.c Thu Sep 17 17:04:16 2009 -*************** -*** 33,38 **** ---- 33,39 ---- - #include "catalog/indexing.h" - #include "catalog/pg_authid.h" - #include "catalog/pg_database.h" -+ #include "catalog/pg_security.h" - #include "catalog/pg_tablespace.h" - #include "commands/comment.h" - #include "commands/dbcommands.h" -*************** -*** 41,46 **** ---- 42,48 ---- - #include "miscadmin.h" - #include "pgstat.h" - #include "postmaster/bgwriter.h" -+ #include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/fd.h" - #include "storage/lmgr.h" -*************** createdb(const CreatedbStmt *stmt) -*** 111,116 **** ---- 113,119 ---- - bool new_record_nulls[Natts_pg_database]; - Oid dboid; - Oid datdba; -+ Oid datsecid; - ListCell *option; - DefElem *dtablespacename = NULL; - DefElem *downer = NULL; -*************** createdb(const CreatedbStmt *stmt) -*** 119,124 **** ---- 122,128 ---- - DefElem *dcollate = NULL; - DefElem *dctype = NULL; - DefElem *dconnlimit = NULL; -+ DefElem *dseclabel = NULL; - char *dbname = stmt->dbname; - char *dbowner = NULL; - const char *dbtemplate = NULL; -*************** createdb(const CreatedbStmt *stmt) -*** 200,205 **** ---- 204,217 ---- - errmsg("LOCATION is not supported anymore"), - errhint("Consider using tablespaces instead."))); - } -+ else if (strcmp(defel->defname, "security_context") == 0) -+ { -+ if (dseclabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SYNTAX_ERROR), -+ errmsg("conflicting or redundant options"))); -+ dseclabel = defel; -+ } - else - elog(ERROR, "option \"%s\" not recognized", - defel->defname); -*************** createdb(const CreatedbStmt *stmt) -*** 272,277 **** ---- 284,292 ---- - - check_is_member_of_role(GetUserId(), datdba); - -+ /* SELinux checks db_database:{create} */ -+ datsecid = sepgsql_database_create(dbname, dseclabel); -+ - /* - * Lookup database (template) to be cloned, and obtain share lock on it. - * ShareLock allows two CREATE DATABASEs to work from the same template -*************** createdb(const CreatedbStmt *stmt) -*** 557,562 **** ---- 572,579 ---- - new_record, new_record_nulls); - - HeapTupleSetOid(tuple, dboid); -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, datsecid); - - simple_heap_insert(pg_database_rel, tuple); - -*************** createdb(const CreatedbStmt *stmt) -*** 573,578 **** ---- 590,598 ---- - /* Create pg_shdepend entries for objects within database */ - copyTemplateDependencies(src_dboid, dboid); - -+ /* Create pg_security entries for objects within database */ -+ securityOnCreateDatabase(src_dboid, dboid); -+ - /* - * Force a checkpoint before starting the copy. This will force dirty - * buffers out to disk, to ensure source database is up-to-date on disk -*************** dropdb(const char *dbname, bool missing_ -*** 776,781 **** ---- 796,804 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - dbname); - -+ /* SELinux checks db_database:{drop} permission */ -+ sepgsql_database_drop(db_id); -+ - /* - * Disallow dropping a DB that is marked istemplate. This is just to - * prevent people from accidentally dropping template0 or template1; they -*************** dropdb(const char *dbname, bool missing_ -*** 829,834 **** ---- 852,862 ---- - dropDatabaseDependencies(db_id); - - /* -+ * Remove pg_security entries for the database. -+ */ -+ securityOnDropDatabase(db_id); -+ -+ /* - * Drop pages for this database that are in the shared buffer cache. This - * is important to ensure that no remaining backend tries to write out a - * dirty buffer to the dead database later... -*************** RenameDatabase(const char *oldname, cons -*** 913,918 **** ---- 941,949 ---- - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to rename database"))); - -+ /* SELinux: check db_database:{setattr} */ -+ sepgsql_database_alter(db_id); -+ - /* - * Make sure the new name doesn't exist. See notes for same error in - * CREATE DATABASE. -*************** movedb(const char *dbname, const char *t -*** 1025,1030 **** ---- 1056,1064 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - dbname); - -+ /* SELinux checks db_database:{setattr} */ -+ sepgsql_database_alter(db_id); -+ - /* - * Obviously can't move the tables of my own database - */ -*************** AlterDatabase(AlterDatabaseStmt *stmt, b -*** 1377,1382 **** ---- 1411,1419 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - stmt->dbname); - -+ /* SELinux checks db_database:{setattr} */ -+ sepgsql_database_alter(HeapTupleGetOid(tuple)); -+ - /* - * Build an updated tuple, perusing the information just obtained - */ -*************** AlterDatabaseSet(AlterDatabaseSetStmt *s -*** 1449,1454 **** ---- 1486,1494 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - stmt->dbname); - -+ /* SELinux checks db_database:{setattr} */ -+ sepgsql_database_alter(HeapTupleGetOid(tuple)); -+ - memset(repl_repl, false, sizeof(repl_repl)); - repl_repl[Anum_pg_database_datconfig - 1] = true; - -*************** AlterDatabaseOwner(const char *dbname, O -*** 1571,1576 **** ---- 1611,1619 ---- - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to change owner of database"))); - -+ /* SELinux checks db_database:{setattr} */ -+ sepgsql_database_alter(HeapTupleGetOid(tuple)); -+ - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); - -*************** AlterDatabaseOwner(const char *dbname, O -*** 1615,1620 **** ---- 1658,1715 ---- - */ - } - -+ /* -+ * ALTER DATABASE name SECURITY_LABEL [=] newlabel -+ */ -+ void -+ AlterDatabaseSecLabel(const char *dbname, DefElem *seclabel) -+ { -+ Relation rel; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ ScanKeyData scankey; -+ SysScanDesc scan; -+ Oid secid; -+ bool replaces[Natts_pg_database]; -+ -+ /* Fetch the old tuple */ -+ rel = heap_open(DatabaseRelationId, RowExclusiveLock); -+ ScanKeyInit(&scankey, -+ Anum_pg_database_datname, -+ BTEqualStrategyNumber, F_NAMEEQ, -+ NameGetDatum(dbname)); -+ scan = systable_beginscan(rel, DatabaseNameIndexId, true, -+ SnapshotNow, 1, &scankey); -+ oldtup = systable_getnext(scan); -+ if (!HeapTupleIsValid(oldtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_DATABASE), -+ errmsg("database \"%s\" does not exist", dbname))); -+ -+ memset(replaces, false, sizeof(replaces)); -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), -+ NULL, NULL, replaces); -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", dbname))); -+ systable_endscan(scan); -+ -+ /* check DAC permission */ -+ if (!pg_database_ownercheck(HeapTupleGetOid(newtup), GetUserId())) -+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); -+ -+ /* SELinux checks db_database:{setattr relabelfrom relabelto} */ -+ secid = sepgsql_database_relabel(HeapTupleGetOid(newtup), seclabel); -+ HeapTupleSetSecid(newtup, secid); -+ -+ simple_heap_update(rel, &newtup->t_self, newtup); -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ -+ heap_close(rel, RowExclusiveLock); -+ } - - /* - * Helper functions -diff -Nrpc base/src/backend/commands/foreigncmds.c sepgsql/src/backend/commands/foreigncmds.c -*** base/src/backend/commands/foreigncmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/foreigncmds.c Thu Sep 17 22:10:19 2009 -*************** -*** 27,32 **** ---- 27,33 ---- - #include "foreign/foreign.h" - #include "miscadmin.h" - #include "parser/parse_func.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -*************** AlterForeignDataWrapperOwner(const char -*** 233,238 **** ---- 234,242 ---- - - if (form->fdwowner != newOwnerId) - { -+ /* SELinux permission check */ -+ sepgsql_fdw_alter(fdwId, InvalidOid); -+ - form->fdwowner = newOwnerId; - - simple_heap_update(rel, &tup->t_self, tup); -*************** AlterForeignServerOwner(const char *name -*** 297,302 **** ---- 301,308 ---- - aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); - } - } -+ /* SELinux permission checks */ -+ sepgsql_foreign_server_alter(srvId); - - form->srvowner = newOwnerId; - -*************** CreateForeignDataWrapper(CreateFdwStmt * -*** 342,347 **** ---- 348,354 ---- - Oid fdwvalidator; - Datum fdwoptions; - Oid ownerId; -+ Oid secid; - - /* Must be super user */ - if (!superuser()) -*************** CreateForeignDataWrapper(CreateFdwStmt * -*** 380,385 **** ---- 387,395 ---- - else - fdwvalidator = InvalidOid; - -+ /* SELinux permission checks */ -+ secid = sepgsql_fdw_create(stmt->fdwname, fdwvalidator); -+ - values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = fdwvalidator; - - nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true; -*************** CreateForeignDataWrapper(CreateFdwStmt * -*** 393,398 **** ---- 403,410 ---- - nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; - - tuple = heap_form_tuple(rel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, secid); - - fdwId = simple_heap_insert(rel, tuple); - CatalogUpdateIndexes(rel, tuple); -*************** AlterForeignDataWrapper(AlterFdwStmt *st -*** 487,492 **** ---- 499,507 ---- - fdwvalidator = DatumGetObjectId(datum); - } - -+ /* SELinux permission checks */ -+ sepgsql_fdw_alter(fdwId, fdwvalidator); -+ - /* - * Options specified, validate and update. - */ -*************** CreateForeignServer(CreateForeignServerS -*** 609,614 **** ---- 624,630 ---- - HeapTuple tuple; - Oid srvId; - Oid ownerId; -+ Oid secid; - AclResult aclresult; - ObjectAddress myself; - ObjectAddress referenced; -*************** CreateForeignServer(CreateForeignServerS -*** 636,641 **** ---- 652,659 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); - -+ secid = sepgsql_foreign_server_create(stmt->fdwname); -+ - /* - * Insert tuple into pg_foreign_server. - */ -*************** CreateForeignServer(CreateForeignServerS -*** 676,681 **** ---- 694,701 ---- - nulls[Anum_pg_foreign_server_srvoptions - 1] = true; - - tuple = heap_form_tuple(rel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, secid); - - srvId = simple_heap_insert(rel, tuple); - -*************** AlterForeignServer(AlterForeignServerStm -*** 732,737 **** ---- 752,760 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, - stmt->servername); - -+ /* SELinux permission checks */ -+ sepgsql_foreign_server_alter(srvId); -+ - memset(repl_val, 0, sizeof(repl_val)); - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); -diff -Nrpc base/src/backend/commands/functioncmds.c sepgsql/src/backend/commands/functioncmds.c -*** base/src/backend/commands/functioncmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/functioncmds.c Thu Sep 17 17:04:16 2009 -*************** -*** 53,58 **** ---- 53,59 ---- - #include "parser/parse_expr.h" - #include "parser/parse_func.h" - #include "parser/parse_type.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -*************** compute_attributes_sql_style(List *optio -*** 517,523 **** - bool *security_definer, - ArrayType **proconfig, - float4 *procost, -! float4 *prorows) - { - ListCell *option; - DefElem *as_item = NULL; ---- 518,525 ---- - bool *security_definer, - ArrayType **proconfig, - float4 *procost, -! float4 *prorows, -! Node **proseclabel) - { - ListCell *option; - DefElem *as_item = NULL; -*************** compute_attributes_sql_style(List *optio -*** 529,534 **** ---- 531,537 ---- - List *set_items = NIL; - DefElem *cost_item = NULL; - DefElem *rows_item = NULL; -+ DefElem *seclabel_item = NULL; - - foreach(option, options) - { -*************** compute_attributes_sql_style(List *optio -*** 558,563 **** ---- 561,574 ---- - errmsg("conflicting or redundant options"))); - windowfunc_item = defel; - } -+ else if (strcmp(defel->defname, "security_context") == 0) -+ { -+ if (seclabel_item) -+ ereport(ERROR, -+ (errcode(ERRCODE_SYNTAX_ERROR), -+ errmsg("conflicting or redundant options"))); -+ seclabel_item = defel; -+ } - else if (compute_common_attribute(defel, - &volatility_item, - &strict_item, -*************** compute_attributes_sql_style(List *optio -*** 622,627 **** ---- 633,640 ---- - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("ROWS must be positive"))); - } -+ if (seclabel_item) -+ *proseclabel = (Node *)seclabel_item; - } - - -*************** CreateFunction(CreateFunctionStmt *stmt, -*** 762,767 **** ---- 775,781 ---- - ArrayType *proconfig; - float4 procost; - float4 prorows; -+ Node *proseclabel; - HeapTuple languageTuple; - Form_pg_language languageStruct; - List *as_clause; -*************** CreateFunction(CreateFunctionStmt *stmt, -*** 784,796 **** - proconfig = NULL; - procost = -1; /* indicates not set */ - prorows = -1; /* indicates not set */ - - /* override attributes from explicit list */ - compute_attributes_sql_style(stmt->options, - &as_clause, &language, - &isWindowFunc, &volatility, - &isStrict, &security, -! &proconfig, &procost, &prorows); - - /* Convert language name to canonical case */ - languageName = case_translate_language_name(language); ---- 798,811 ---- - proconfig = NULL; - procost = -1; /* indicates not set */ - prorows = -1; /* indicates not set */ -+ proseclabel = NULL; - - /* override attributes from explicit list */ - compute_attributes_sql_style(stmt->options, - &as_clause, &language, - &isWindowFunc, &volatility, - &isStrict, &security, -! &proconfig, &procost, &prorows, &proseclabel); - - /* Convert language name to canonical case */ - languageName = case_translate_language_name(language); -*************** CreateFunction(CreateFunctionStmt *stmt, -*** 926,932 **** - parameterDefaults, - PointerGetDatum(proconfig), - procost, -! prorows); - } - - ---- 941,948 ---- - parameterDefaults, - PointerGetDatum(proconfig), - procost, -! prorows, -! proseclabel); - } - - -*************** RenameFunction(List *name, List *argtype -*** 1112,1117 **** ---- 1128,1136 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux permission checks */ -+ sepgsql_proc_alter(procOid, newname, InvalidOid); -+ - /* rename */ - namestrcpy(&(procForm->proname), newname); - simple_heap_update(rel, &tup->t_self, tup); -*************** AlterFunctionOwner_internal(Relation rel -*** 1220,1225 **** ---- 1239,1246 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(procForm->pronamespace)); - } -+ /* SELinux permission checks */ -+ sepgsql_proc_alter(procOid, NULL, InvalidOid); - - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); -*************** AlterFunctionOwner_internal(Relation rel -*** 1258,1263 **** ---- 1279,1337 ---- - } - - /* -+ * ALTER FUNCTION name(args,...) SECURITY_LABEL [=] newlabel -+ */ -+ void -+ AlterFunctionSecLabel(List *name, List *argtypes, DefElem *seclabel) -+ { -+ Relation rel; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ Oid procOid; -+ Oid secid; -+ bool replaces[Natts_pg_proc]; -+ -+ /* open pg_proc system catalog */ -+ rel = heap_open(ProcedureRelationId, RowExclusiveLock); -+ -+ procOid = LookupFuncNameTypeNames(name, argtypes, false); -+ -+ oldtup = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(procOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(oldtup)) -+ elog(ERROR, "cache lookup failed for function %u", procOid); -+ -+ memset(replaces, false, sizeof(replaces)); -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), -+ NULL, NULL, replaces); -+ -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", -+ get_func_name(procOid)))); -+ -+ ReleaseSysCache(oldtup); -+ -+ /* DAC permission checks */ -+ if (!pg_proc_ownercheck(HeapTupleGetOid(newtup), GetUserId())) -+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, -+ get_func_name(HeapTupleGetOid(newtup))); -+ -+ /* SELinux permission checks */ -+ secid = sepgsql_proc_relabel(procOid, seclabel); -+ HeapTupleSetSecid(newtup, secid); -+ -+ simple_heap_update(rel, &newtup->t_self, newtup); -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ -+ heap_close(rel, RowExclusiveLock); -+ } -+ -+ /* - * Implements the ALTER FUNCTION utility command (except for the - * RENAME and OWNER clauses, which are handled as part of the generic - * ALTER framework). -*************** AlterFunction(AlterFunctionStmt *stmt) -*** 1296,1301 **** ---- 1370,1378 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - NameListToString(stmt->func->funcname)); - -+ /* SELinux checks permissions */ -+ sepgsql_proc_alter(funcOid, NULL, InvalidOid); -+ - if (procForm->proisagg) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -*************** CreateCast(CreateCastStmt *stmt) -*** 1473,1478 **** ---- 1550,1556 ---- - char sourcetyptype; - char targettyptype; - Oid funcid; -+ Oid secid; - int nargs; - char castcontext; - char castmethod; -*************** CreateCast(CreateCastStmt *stmt) -*** 1674,1679 **** ---- 1752,1759 ---- - castcontext = 0; /* keep compiler quiet */ - break; - } -+ /* SELinux permission check */ -+ secid = sepgsql_cast_create(sourcetypeid, targettypeid, funcid); - - relation = heap_open(CastRelationId, RowExclusiveLock); - -*************** CreateCast(CreateCastStmt *stmt) -*** 1704,1709 **** ---- 1784,1792 ---- - - tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls); - -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, secid); -+ - simple_heap_insert(relation, tuple); - - CatalogUpdateIndexes(relation, tuple); -*************** AlterFunctionNamespace(List *name, List -*** 1897,1902 **** ---- 1980,1988 ---- - NameStr(proc->proname), - newschema))); - -+ /* SELinux checks permissions */ -+ sepgsql_proc_alter(procOid, NULL, nspOid); -+ - /* OK, modify the pg_proc row */ - - /* tup is a copy, so we can scribble directly on it */ -diff -Nrpc base/src/backend/commands/indexcmds.c sepgsql/src/backend/commands/indexcmds.c -*** base/src/backend/commands/indexcmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/indexcmds.c Fri Sep 18 17:02:48 2009 -*************** -*** 37,42 **** ---- 37,43 ---- - #include "parser/parse_coerce.h" - #include "parser/parse_func.h" - #include "parser/parsetree.h" -+ #include "security/sepgsql.h" - #include "storage/lmgr.h" - #include "storage/proc.h" - #include "storage/procarray.h" -*************** DefineIndex(RangeVar *heapRelation, -*** 258,263 **** ---- 259,267 ---- - } - } - -+ /* SELinux checks */ -+ sepgsql_index_create(relationId, namespaceId, check_rights); -+ - /* - * look up the access method, verify it can handle the requested features - */ -diff -Nrpc base/src/backend/commands/lockcmds.c sepgsql/src/backend/commands/lockcmds.c -*** base/src/backend/commands/lockcmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/lockcmds.c Fri Sep 18 14:51:00 2009 -*************** -*** 20,25 **** ---- 20,26 ---- - #include "commands/lockcmds.h" - #include "miscadmin.h" - #include "parser/parse_clause.h" -+ #include "security/sepgsql.h" - #include "storage/lmgr.h" - #include "utils/acl.h" - #include "utils/lsyscache.h" -*************** LockTableRecurse(Oid reloid, RangeVar *r -*** 140,145 **** ---- 141,149 ---- - errmsg("\"%s\" is not a table", - RelationGetRelationName(rel)))); - -+ /* SELinux: check db_table:{lock} permission */ -+ sepgsql_relation_lock(reloid); -+ - /* - * If requested, recurse to children. We use find_inheritance_children - * not find_all_inheritors to avoid taking locks far in advance of -diff -Nrpc base/src/backend/commands/opclasscmds.c sepgsql/src/backend/commands/opclasscmds.c -*** base/src/backend/commands/opclasscmds.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/commands/opclasscmds.c Thu Sep 17 17:04:16 2009 -*************** -*** 35,40 **** ---- 35,41 ---- - #include "parser/parse_func.h" - #include "parser/parse_oper.h" - #include "parser/parse_type.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -*************** CreateOpFamily(char *amname, char *opfna -*** 177,182 **** ---- 178,184 ---- - HeapTuple tup; - Datum values[Natts_pg_opfamily]; - bool nulls[Natts_pg_opfamily]; -+ Oid opfSecid; - NameData opfName; - ObjectAddress myself, - referenced; -*************** CreateOpFamily(char *amname, char *opfna -*** 197,202 **** ---- 199,207 ---- - errmsg("operator family \"%s\" for access method \"%s\" already exists", - opfname, amname))); - -+ /* SELinux check permission */ -+ opfSecid = sepgsql_opfamily_create(opfname, namespaceoid); -+ - /* - * Okay, let's create the pg_opfamily entry. - */ -*************** CreateOpFamily(char *amname, char *opfna -*** 210,215 **** ---- 215,222 ---- - values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId()); - - tup = heap_form_tuple(rel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, opfSecid); - - opfamilyoid = simple_heap_insert(rel, tup); - -*************** DefineOpClass(CreateOpClassStmt *stmt) -*** 265,270 **** ---- 272,278 ---- - Form_pg_am pg_am; - Datum values[Natts_pg_opclass]; - bool nulls[Natts_pg_opclass]; -+ Oid opcSecid; - AclResult aclresult; - NameData opcName; - ObjectAddress myself, -*************** DefineOpClass(CreateOpClassStmt *stmt) -*** 353,358 **** ---- 361,369 ---- - NameListToString(stmt->opfamilyname), stmt->amname))); - opfamilyoid = HeapTupleGetOid(tup); - -+ /* SELinux checks permission */ -+ sepgsql_opfamily_alter(opfamilyoid, NULL); -+ - /* - * XXX given the superuser check above, there's no need for an - * ownership check here -*************** DefineOpClass(CreateOpClassStmt *stmt) -*** 371,376 **** ---- 382,390 ---- - { - opfamilyoid = HeapTupleGetOid(tup); - -+ /* SELinux checks permission */ -+ sepgsql_opfamily_alter(opfamilyoid, NULL); -+ - /* - * XXX given the superuser check above, there's no need for an - * ownership check here -*************** DefineOpClass(CreateOpClassStmt *stmt) -*** 441,446 **** ---- 455,462 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - get_func_name(funcOid)); - #endif -+ /* SELinux check permission */ -+ sepgsql_opfamily_add_operator(opfamilyoid, operOid); - - /* Save the info */ - member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); -*************** DefineOpClass(CreateOpClassStmt *stmt) -*** 465,470 **** ---- 481,488 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - get_func_name(funcOid)); - #endif -+ /* SELinux check permission */ -+ sepgsql_opfamily_add_procedure(opfamilyoid, funcOid); - - /* Save the info */ - member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); -*************** DefineOpClass(CreateOpClassStmt *stmt) -*** 531,536 **** ---- 549,557 ---- - errmsg("operator class \"%s\" for access method \"%s\" already exists", - opcname, stmt->amname))); - -+ /* SELinux permission check */ -+ opcSecid = sepgsql_opclass_create(opcname, namespaceoid); -+ - /* - * If we are creating a default opclass, check there isn't one already. - * (Note we do not restrict this test to visible opclasses; this ensures -*************** DefineOpFamily(CreateOpFamilyStmt *stmt) -*** 657,662 **** ---- 678,684 ---- - HeapTuple tup; - Datum values[Natts_pg_opfamily]; - bool nulls[Natts_pg_opfamily]; -+ Oid opfSecid; - AclResult aclresult; - NameData opfName; - ObjectAddress myself, -*************** DefineOpFamily(CreateOpFamilyStmt *stmt) -*** 699,704 **** ---- 721,729 ---- - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to create an operator family"))); - -+ /* SELinux permission check */ -+ opfSecid = sepgsql_opfamily_create(opfname, namespaceoid); -+ - rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock); - - /* -*************** AlterOpFamily(AlterOpFamilyStmt *stmt) -*** 773,778 **** ---- 798,804 ---- - int maxOpNumber, /* amstrategies value */ - maxProcNumber; /* amsupport value */ - HeapTuple tup; -+ Oid opfSecid; - Form_pg_am pg_am; - - /* Get necessary info about access method */ -*************** AlterOpFamily(AlterOpFamilyStmt *stmt) -*** 805,810 **** ---- 831,837 ---- - errmsg("operator family \"%s\" does not exist for access method \"%s\"", - NameListToString(stmt->opfamilyname), stmt->amname))); - opfamilyoid = HeapTupleGetOid(tup); -+ opfSecid = HeapTupleGetSecid(tup); - ReleaseSysCache(tup); - - /* -*************** AlterOpFamily(AlterOpFamilyStmt *stmt) -*** 817,822 **** ---- 844,852 ---- - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to alter an operator family"))); - -+ /* SELinux permission checks */ -+ sepgsql_opfamily_alter(opfamilyoid, NULL); -+ - /* - * ADD and DROP cases need separate code from here on down. - */ -*************** AlterOpFamilyAdd(List *opfamilyname, Oid -*** 893,898 **** ---- 923,930 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - get_func_name(funcOid)); - #endif -+ /* SELinux permission check */ -+ sepgsql_opfamily_add_operator(opfamilyoid, operOid); - - /* Save the info */ - member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); -*************** AlterOpFamilyAdd(List *opfamilyname, Oid -*** 917,922 **** ---- 949,956 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - get_func_name(funcOid)); - #endif -+ /* SELinux permission check */ -+ sepgsql_opfamily_add_procedure(opfamilyoid, funcOid); - - /* Save the info */ - member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); -*************** RenameOpClass(List *name, const char *ac -*** 1815,1820 **** ---- 1849,1857 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux permission checks */ -+ sepgsql_opclass_alter(opcOid, newname); -+ - /* rename */ - namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname); - simple_heap_update(rel, &tup->t_self, tup); -*************** RenameOpFamily(List *name, const char *a -*** 1915,1920 **** ---- 1952,1960 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux check permissions */ -+ sepgsql_opfamily_alter(opfOid, newname); -+ - /* rename */ - namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); - simple_heap_update(rel, &tup->t_self, tup); -*************** AlterOpClassOwner_internal(Relation rel, -*** 2035,2040 **** ---- 2075,2082 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux permission check */ -+ sepgsql_opclass_alter(HeapTupleGetOid(tup), NULL); - - /* - * Modify the owner --- okay to scribble on tup because it's a copy -*************** AlterOpFamilyOwner_internal(Relation rel -*** 2162,2167 **** ---- 2204,2211 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux permission checks */ -+ sepgsql_opfamily_alter(HeapTupleGetOid(tup), NULL); - - /* - * Modify the owner --- okay to scribble on tup because it's a copy -diff -Nrpc base/src/backend/commands/operatorcmds.c sepgsql/src/backend/commands/operatorcmds.c -*** base/src/backend/commands/operatorcmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/operatorcmds.c Thu Sep 17 22:10:19 2009 -*************** -*** 45,50 **** ---- 45,51 ---- - #include "parser/parse_func.h" - #include "parser/parse_oper.h" - #include "parser/parse_type.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/lsyscache.h" - #include "utils/rel.h" -*************** AlterOperatorOwner_internal(Relation rel -*** 432,437 **** ---- 433,440 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(oprForm->oprnamespace)); - } -+ /* SELinux permission check */ -+ sepgsql_operator_alter(operOid); - - /* - * Modify the owner --- okay to scribble on tup because it's a copy -diff -Nrpc base/src/backend/commands/proclang.c sepgsql/src/backend/commands/proclang.c -*** base/src/backend/commands/proclang.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/proclang.c Thu Sep 17 22:10:19 2009 -*************** -*** 30,35 **** ---- 30,36 ---- - #include "miscadmin.h" - #include "parser/gramparse.h" - #include "parser/parse_func.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -*************** CreateProceduralLanguage(CreatePLangStmt -*** 151,157 **** - NIL, - PointerGetDatum(NULL), - 1, -! 0); - } - - /* ---- 152,159 ---- - NIL, - PointerGetDatum(NULL), - 1, -! 0, -! NULL); - } - - /* -*************** CreateProceduralLanguage(CreatePLangStmt -*** 186,192 **** - NIL, - PointerGetDatum(NULL), - 1, -! 0); - } - } - else ---- 188,195 ---- - NIL, - PointerGetDatum(NULL), - 1, -! 0, -! NULL); - } - } - else -*************** create_proc_lang(const char *languageNam -*** 275,284 **** ---- 278,293 ---- - bool nulls[Natts_pg_language]; - NameData langname; - HeapTuple tup; -+ Oid langSecid; - ObjectAddress myself, - referenced; - - /* -+ * SELinux permission checks -+ */ -+ langSecid = sepgsql_language_create(languageName, handlerOid, valOid); -+ -+ /* - * Insert the new language into pg_language - */ - rel = heap_open(LanguageRelationId, RowExclusiveLock); -*************** create_proc_lang(const char *languageNam -*** 297,302 **** ---- 306,313 ---- - nulls[Anum_pg_language_lanacl - 1] = true; - - tup = heap_form_tuple(tupDesc, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, langSecid); - - simple_heap_insert(rel, tup); - -*************** RenameLanguage(const char *oldname, cons -*** 518,523 **** ---- 529,537 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, - oldname); - -+ /* SELinux permission checks */ -+ sepgsql_language_alter(HeapTupleGetOid(tup)); -+ - /* rename */ - namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname); - simple_heap_update(rel, &tup->t_self, tup); -*************** AlterLanguageOwner_internal(HeapTuple tu -*** 613,618 **** ---- 627,635 ---- - /* Must be able to become new owner */ - check_is_member_of_role(GetUserId(), newOwnerId); - -+ /* SELinux permission checks */ -+ sepgsql_language_alter(HeapTupleGetOid(tup)); -+ - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); - -diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/schemacmds.c -*** base/src/backend/commands/schemacmds.c 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 base/src/backend/commands/sequence.c sepgsql/src/backend/commands/sequence.c -*** base/src/backend/commands/sequence.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/sequence.c Fri Sep 18 14:51:00 2009 -*************** -*** 26,31 **** ---- 26,32 ---- - #include "commands/tablecmds.h" - #include "miscadmin.h" - #include "nodes/makefuncs.h" -+ #include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/lmgr.h" - #include "storage/proc.h" -*************** DefineSequence(CreateSeqStmt *seq) -*** 201,206 **** ---- 202,208 ---- - stmt->options = list_make1(defWithOids(false)); - stmt->oncommit = ONCOMMIT_NOOP; - stmt->tablespacename = NULL; -+ stmt->secLabel = seq->secLabel; - - seqoid = DefineRelation(stmt, RELKIND_SEQUENCE); - -*************** AlterSequence(AlterSeqStmt *stmt) -*** 328,333 **** ---- 330,337 ---- - if (!pg_class_ownercheck(relid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - stmt->sequence->relname); -+ /* SELinux checks db_sequence:{setattr} */ -+ sepgsql_relation_alter(relid, NULL, InvalidOid); - - /* do the work */ - AlterSequenceInternal(relid, stmt->options); -*************** nextval_internal(Oid relid) -*** 467,472 **** ---- 471,479 ---- - errmsg("permission denied for sequence %s", - RelationGetRelationName(seqrel)))); - -+ /* SELinux check db_sequence:{next_value} */ -+ sepgsql_sequence_next_value(elm->relid); -+ - if (elm->last != elm->cached) /* some numbers were cached */ - { - Assert(elm->last_valid); -*************** currval_oid(PG_FUNCTION_ARGS) -*** 662,667 **** ---- 669,677 ---- - errmsg("permission denied for sequence %s", - RelationGetRelationName(seqrel)))); - -+ /* SELinux check db_sequence:{get_value} */ -+ sepgsql_sequence_get_value(elm->relid); -+ - if (!elm->last_valid) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), -*************** lastval(PG_FUNCTION_ARGS) -*** 706,711 **** ---- 716,724 ---- - errmsg("permission denied for sequence %s", - RelationGetRelationName(seqrel)))); - -+ /* SELinux check db_sequence:{get_value} */ -+ sepgsql_sequence_get_value(last_used_seq->relid); -+ - result = last_used_seq->last; - relation_close(seqrel, NoLock); - -*************** do_setval(Oid relid, int64 next, bool is -*** 742,747 **** ---- 755,763 ---- - errmsg("permission denied for sequence %s", - RelationGetRelationName(seqrel)))); - -+ /* SELinux check db_sequence:{set_value} */ -+ sepgsql_sequence_set_value(elm->relid); -+ - /* lock page' buffer and read tuple */ - seq = read_info(elm, seqrel, &buf); - -diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/tablecmds.c -*** base/src/backend/commands/tablecmds.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/backend/commands/tablecmds.c Tue Dec 15 17:30:25 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 -*************** checkFkeyPermissions(Relation rel, int16 -*** 5112,5118 **** - aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid, - ACL_REFERENCES); - if (aclresult == ACLCHECK_OK) -! return; - /* Else we must have REFERENCES on each column */ - for (i = 0; i < natts; i++) - { ---- 5166,5172 ---- - aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid, - ACL_REFERENCES); - if (aclresult == ACLCHECK_OK) -! goto ok; - /* Else we must have REFERENCES on each column */ - for (i = 0; i < natts; i++) - { -*************** checkFkeyPermissions(Relation rel, int16 -*** 5122,5127 **** ---- 5176,5184 ---- - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(rel)); - } -+ ok: -+ /* SELinux: check db_table/db_column:{reference} */ -+ sepgsql_relation_references(rel, attnums, natts); - } - - /* -*************** ATExecDropConstraint(Relation rel, const -*** 5385,5391 **** - - /* At top level, permission check was done in ATPrepCmd, else do it */ - if (recursing) -! ATSimplePermissions(rel, false); - - conrel = heap_open(ConstraintRelationId, RowExclusiveLock); - ---- 5442,5448 ---- - - /* 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 -*** 6318,6323 **** ---- 6375,6382 ---- - 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 -*** 6922,6928 **** - * 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) ---- 6981,6987 ---- - * 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, -*** 7580,7585 **** ---- 7639,7647 ---- - 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 -*** 7772,7777 **** ---- 7834,7967 ---- - relation_close(depRel, AccessShareLock); - } - -+ /* -+ * ALTER TABLE/SEQUENCE name SECURITY_LABEL [=] newlabel -+ * ALTER TABLE/SEQUENCE name ALTER column SECURITY_LABEL [=] newlabel -+ */ -+ static void -+ ExecRelationSetSecLabel(Oid relid, DefElem *seclabel) -+ { -+ Relation rel; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ Oid secid; -+ bool replaces[Natts_pg_class]; -+ -+ rel = heap_open(RelationRelationId, RowExclusiveLock); -+ oldtup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(oldtup)) -+ elog(ERROR, "cache lookup failed for relation: %u", relid); -+ -+ memset(replaces, false, sizeof(replaces)); -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), -+ NULL, NULL, replaces); -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", -+ get_rel_name(relid)))); -+ -+ ReleaseSysCache(oldtup); -+ -+ /* SELinux checks db_table:{setattr relabelfrom relabelto} */ -+ secid = sepgsql_relation_relabel(relid, seclabel); -+ -+ HeapTupleSetSecid(newtup, secid); -+ -+ simple_heap_update(rel, &newtup->t_self, newtup); -+ -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ -+ heap_close(rel, RowExclusiveLock); -+ } -+ -+ static void -+ ExecAttributeSetSecLabel(Oid relid, const char *attname, DefElem *seclabel) -+ { -+ Relation rel; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ AttrNumber attnum; -+ Oid secid; -+ bool replaces[Natts_pg_attribute]; -+ -+ rel = heap_open(AttributeRelationId, RowExclusiveLock); -+ oldtup = SearchSysCacheAttName(relid, attname); -+ if (!HeapTupleIsValid(oldtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_COLUMN), -+ errmsg("column \"%s\" of relation \"%s\" does not exist", -+ attname, get_rel_name(relid)))); -+ attnum = ((Form_pg_attribute) GETSTRUCT(oldtup))->attnum; -+ -+ memset(replaces, false, sizeof(replaces)); -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), -+ NULL, NULL, replaces); -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security context on \"%s.%s\"", -+ get_rel_name(relid), attname))); -+ -+ ReleaseSysCache(oldtup); -+ -+ /* SELinux checks db_column:{setattr relabelfrom relabelto} */ -+ secid = sepgsql_attribute_relabel(relid, attnum, seclabel); -+ -+ HeapTupleSetSecid(newtup, secid); -+ -+ simple_heap_update(rel, &newtup->t_self, newtup); -+ -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ -+ heap_close(rel, RowExclusiveLock); -+ } -+ -+ void -+ AlterRelationSecLabel(RangeVar *relation, const char *attname, -+ ObjectType objtype, DefElem *seclabel) -+ { -+ Oid relid; -+ char relkind; -+ -+ /* Check relation type against type specified in the ALTER command */ -+ relid = RangeVarGetRelid(relation, false); -+ relkind = get_rel_relkind(relid); -+ -+ switch (objtype) -+ { -+ case OBJECT_TABLE: -+ case OBJECT_COLUMN: -+ if (relkind != RELKIND_RELATION) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not a table", get_rel_name(relid)))); -+ break; -+ -+ case OBJECT_SEQUENCE: -+ if (relkind != RELKIND_SEQUENCE) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not a sequence", get_rel_name(relid)))); -+ break; -+ -+ default: -+ elog(ERROR, "unrecognized object type: %d", (int)objtype); -+ break; -+ } -+ -+ /* Exec set security label */ -+ if (objtype != OBJECT_COLUMN) -+ ExecRelationSetSecLabel(relid, seclabel); -+ else -+ ExecAttributeSetSecLabel(relid, attname, seclabel); -+ } - - /* - * This code supports -diff -Nrpc base/src/backend/commands/trigger.c sepgsql/src/backend/commands/trigger.c -*** base/src/backend/commands/trigger.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/backend/commands/trigger.c Tue Dec 15 17:30:25 2009 -*************** -*** 33,38 **** ---- 33,39 ---- - #include "nodes/makefuncs.h" - #include "parser/parse_func.h" - #include "pgstat.h" -+ #include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "tcop/utility.h" - #include "utils/acl.h" -*************** CreateTrigger(CreateTrigStmt *stmt, Oid -*** 182,187 **** ---- 183,192 ---- - NameListToString(stmt->funcname)))); - } - -+ /* SELinux checks */ -+ if (checkPermissions) -+ sepgsql_trigger_create(RelationGetRelid(rel), stmt->trigname, funcoid); -+ - /* - * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that - * references one of the built-in RI_FKey trigger functions, assume it is -*************** DropTrigger(Oid relid, const char *trign -*** 746,751 **** ---- 751,757 ---- - if (!pg_class_ownercheck(relid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - get_rel_name(relid)); -+ sepgsql_trigger_drop(relid, trigname); - - object.classId = TriggerRelationId; - object.objectId = HeapTupleGetOid(tup); -*************** renametrig(Oid relid, -*** 862,867 **** ---- 868,876 ---- - */ - targetrel = heap_open(relid, AccessExclusiveLock); - -+ /* SELinux checks */ -+ sepgsql_trigger_alter(relid, oldname); -+ - /* - * Scan pg_trigger twice for existing triggers on relation. We do this in - * order to ensure a trigger does not exist with newname (The unique index -diff -Nrpc base/src/backend/commands/tsearchcmds.c sepgsql/src/backend/commands/tsearchcmds.c -*** base/src/backend/commands/tsearchcmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/tsearchcmds.c Thu Sep 17 23:44:07 2009 -*************** -*** 35,40 **** ---- 35,41 ---- - #include "miscadmin.h" - #include "nodes/makefuncs.h" - #include "parser/parse_func.h" -+ #include "security/sepgsql.h" - #include "tsearch/ts_cache.h" - #include "tsearch/ts_public.h" - #include "tsearch/ts_utils.h" -*************** DefineTSParser(List *names, List *parame -*** 171,176 **** ---- 172,178 ---- - NameData pname; - Oid prsOid; - Oid namespaceoid; -+ Oid secid; - - if (!superuser()) - ereport(ERROR, -*************** DefineTSParser(List *names, List *parame -*** 250,261 **** ---- 252,273 ---- - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("text search parser lextypes method is required"))); - -+ /* Permission checks */ -+ secid = sepgsql_ts_parser_create(prsname, namespaceoid, -+ DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_parser_prsheadline - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])); -+ - /* - * Looks good, insert - */ - prsRel = heap_open(TSParserRelationId, RowExclusiveLock); - - tup = heap_form_tuple(prsRel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, secid); - - prsOid = simple_heap_insert(prsRel, tup); - -*************** RenameTSParser(List *oldname, const char -*** 372,377 **** ---- 384,392 ---- - - prsId = TSParserGetPrsid(oldname, false); - -+ /* SELinux checks */ -+ sepgsql_ts_parser_alter(prsId, newname); -+ - tup = SearchSysCacheCopy(TSPARSEROID, - ObjectIdGetDatum(prsId), - 0, 0, 0); -*************** DefineTSDictionary(List *names, List *pa -*** 503,508 **** ---- 518,524 ---- - List *dictoptions = NIL; - Oid dictOid; - Oid namespaceoid; -+ Oid secid; - AclResult aclresult; - char *dictname; - -*************** DefineTSDictionary(List *names, List *pa -*** 515,520 **** ---- 531,539 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceoid)); - -+ /* SELinux check */ -+ secid = sepgsql_ts_dict_create(dictname, namespaceoid); -+ - /* - * loop over the definition list and extract the information we need. - */ -*************** DefineTSDictionary(List *names, List *pa -*** 563,568 **** ---- 582,589 ---- - dictRel = heap_open(TSDictionaryRelationId, RowExclusiveLock); - - tup = heap_form_tuple(dictRel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, secid); - - dictOid = simple_heap_insert(dictRel, tup); - -*************** RenameTSDictionary(List *oldname, const -*** 621,626 **** ---- 642,650 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux checks */ -+ sepgsql_ts_dict_alter(dictId, newname); -+ - namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname); - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); -*************** AlterTSDictionary(AlterTSDictionaryStmt -*** 762,767 **** ---- 786,794 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, - NameListToString(stmt->dictname)); - -+ /* SELinux checks */ -+ sepgsql_ts_dict_alter(dictId, NULL); -+ - /* deserialize the existing set of options */ - opt = SysCacheGetAttr(TSDICTOID, tup, - Anum_pg_ts_dict_dictinitoption, -*************** AlterTSDictionaryOwner(List *name, Oid n -*** 889,894 **** ---- 916,923 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux checks */ -+ sepgsql_ts_dict_alter(dictId, NULL); - - form->dictowner = newOwnerId; - -*************** DefineTSTemplate(List *names, List *para -*** 999,1004 **** ---- 1028,1034 ---- - NameData dname; - int i; - Oid dictOid; -+ Oid dictSecid; - Oid namespaceoid; - char *tmplname; - -*************** DefineTSTemplate(List *names, List *para -*** 1054,1059 **** ---- 1084,1094 ---- - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("text search template lexize method is required"))); - -+ /* SELinux checks */ -+ dictSecid = sepgsql_ts_template_create(tmplname, namespaceoid, -+ DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])); -+ - /* - * Looks good, insert - */ -*************** DefineTSTemplate(List *names, List *para -*** 1061,1066 **** ---- 1096,1103 ---- - tmplRel = heap_open(TSTemplateRelationId, RowExclusiveLock); - - tup = heap_form_tuple(tmplRel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, dictSecid); - - dictOid = simple_heap_insert(tmplRel, tup); - -*************** RenameTSTemplate(List *oldname, const ch -*** 1093,1098 **** ---- 1130,1138 ---- - - tmplId = TSTemplateGetTmplid(oldname, false); - -+ /* Permission checks */ -+ sepgsql_ts_template_alter(tmplId, newname); -+ - tup = SearchSysCacheCopy(TSTEMPLATEOID, - ObjectIdGetDatum(tmplId), - 0, 0, 0); -*************** DefineTSConfiguration(List *names, List -*** 1335,1340 **** ---- 1375,1381 ---- - Oid sourceOid = InvalidOid; - Oid prsOid = InvalidOid; - Oid cfgOid; -+ Oid cfgSecid; - ListCell *pl; - - /* Convert list of names to a name and namespace */ -*************** DefineTSConfiguration(List *names, List -*** 1399,1404 **** ---- 1440,1448 ---- - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("text search parser is required"))); - -+ /* SELinux checks */ -+ cfgSecid = sepgsql_ts_config_create(cfgname, namespaceoid); -+ - /* - * Looks good, build tuple and insert - */ -*************** DefineTSConfiguration(List *names, List -*** 1414,1419 **** ---- 1458,1465 ---- - cfgRel = heap_open(TSConfigRelationId, RowExclusiveLock); - - tup = heap_form_tuple(cfgRel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, cfgSecid); - - cfgOid = simple_heap_insert(cfgRel, tup); - -*************** RenameTSConfiguration(List *oldname, con -*** 1519,1524 **** ---- 1565,1573 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* permission checks */ -+ sepgsql_ts_config_alter(cfgId, newname); -+ - namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname); - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); -*************** AlterTSConfigurationOwner(List *name, Oi -*** 1690,1695 **** ---- 1739,1746 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux checks */ -+ sepgsql_ts_config_alter(cfgId, NULL); - - form->cfgowner = newOwnerId; - -*************** AlterTSConfiguration(AlterTSConfiguratio -*** 1727,1732 **** ---- 1778,1786 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, - NameListToString(stmt->cfgname)); - -+ /* SELinux checks */ -+ sepgsql_ts_config_alter(HeapTupleGetOid(tup), NULL); -+ - relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock); - - /* Add or drop mappings */ -diff -Nrpc base/src/backend/commands/typecmds.c sepgsql/src/backend/commands/typecmds.c -*** base/src/backend/commands/typecmds.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/typecmds.c Thu Sep 17 22:10:19 2009 -*************** -*** 56,61 **** ---- 56,62 ---- - #include "parser/parse_expr.h" - #include "parser/parse_func.h" - #include "parser/parse_type.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -*************** AlterDomainDefault(List *names, Node *de -*** 1543,1548 **** ---- 1544,1550 ---- - - /* Check it's a domain and check user has permission for ALTER DOMAIN */ - checkDomainOwner(tup, typename); -+ sepgsql_type_alter(domainoid, NULL, InvalidOid); - - /* Setup new tuple */ - MemSet(new_record, (Datum) 0, sizeof(new_record)); -*************** AlterDomainNotNull(List *names, bool not -*** 1671,1676 **** ---- 1673,1679 ---- - - /* Check it's a domain and check user has permission for ALTER DOMAIN */ - checkDomainOwner(tup, typename); -+ sepgsql_type_alter(domainoid, NULL, InvalidOid); - - /* Is the domain already set to the desired constraint? */ - if (typTup->typnotnull == notNull) -*************** AlterDomainDropConstraint(List *names, c -*** 1772,1777 **** ---- 1775,1781 ---- - - /* Check it's a domain and check user has permission for ALTER DOMAIN */ - checkDomainOwner(tup, typename); -+ sepgsql_type_alter(domainoid, NULL, InvalidOid); - - /* Grab an appropriate lock on the pg_constraint relation */ - conrel = heap_open(ConstraintRelationId, RowExclusiveLock); -*************** AlterDomainAddConstraint(List *names, No -*** 1848,1853 **** ---- 1852,1858 ---- - - /* Check it's a domain and check user has permission for ALTER DOMAIN */ - checkDomainOwner(tup, typename); -+ sepgsql_type_alter(domainoid, NULL, InvalidOid); - - /* Check for unsupported constraint types */ - if (IsA(newConstraint, FkConstraint)) -*************** RenameType(List *names, const char *newT -*** 2470,2475 **** ---- 2475,2483 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, - format_type_be(typeOid)); - -+ /* SELinux check permission */ -+ sepgsql_type_alter(typeOid, newTypeName, InvalidOid); -+ - /* - * If it's a composite type, we need to check that it really is a - * free-standing composite type, and not a table's rowtype. We want people -*************** AlterTypeOwner(List *names, Oid newOwner -*** 2590,2595 **** ---- 2598,2605 ---- - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(typTup->typnamespace)); - } -+ /* SELinux checks permissions */ -+ sepgsql_type_alter(HeapTupleGetOid(tup), NULL, InvalidOid); - - /* - * If it's a composite type, invoke ATExecChangeOwner so that we fix -*************** AlterTypeNamespace(List *names, const ch -*** 2706,2711 **** ---- 2716,2724 ---- - errhint("You can alter type %s, which will alter the array type as well.", - format_type_be(elemOid)))); - -+ /* SELinux checks permissions */ -+ sepgsql_type_alter(typeOid, NULL, nspOid); -+ - /* and do the work */ - AlterTypeNamespaceInternal(typeOid, nspOid, false, true); - } -diff -Nrpc base/src/backend/commands/view.c sepgsql/src/backend/commands/view.c -*** base/src/backend/commands/view.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/commands/view.c Fri Sep 18 14:51:00 2009 -*************** -*** 28,33 **** ---- 28,34 ---- - #include "rewrite/rewriteDefine.h" - #include "rewrite/rewriteManip.h" - #include "rewrite/rewriteSupport.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -*************** DefineVirtualRelation(const RangeVar *re -*** 166,171 **** ---- 167,175 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(rel)); - -+ /* SELinux checks */ -+ sepgsql_view_replace(viewOid); -+ - /* Also check it's not in use already */ - CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW"); - -diff -Nrpc base/src/backend/executor/execJunk.c sepgsql/src/backend/executor/execJunk.c -*** base/src/backend/executor/execJunk.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/executor/execJunk.c Wed Jul 15 19:30:50 2009 -*************** -*** 60,66 **** - * An optional resultSlot can be passed as well. - */ - JunkFilter * -! ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) - { - JunkFilter *junkfilter; - TupleDesc cleanTupType; ---- 60,67 ---- - * An optional resultSlot can be passed as well. - */ - JunkFilter * -! ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel, -! TupleTableSlot *slot) - { - JunkFilter *junkfilter; - TupleDesc cleanTupType; -*************** ExecInitJunkFilter(List *targetList, boo -*** 72,78 **** - /* - * Compute the tuple descriptor for the cleaned tuple. - */ -! cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); - - /* - * Use the given slot, or make a new slot if we weren't given one. ---- 73,79 ---- - /* - * Compute the tuple descriptor for the cleaned tuple. - */ -! cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hasseclabel); - - /* - * Use the given slot, or make a new slot if we weren't given one. -diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/execMain.c -*** base/src/backend/executor/execMain.c 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 base/src/backend/executor/execQual.c sepgsql/src/backend/executor/execQual.c -*** base/src/backend/executor/execQual.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/executor/execQual.c Thu Sep 17 17:04:16 2009 -*************** -*** 47,52 **** ---- 47,53 ---- - #include "nodes/nodeFuncs.h" - #include "optimizer/planner.h" - #include "pgstat.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -*************** init_fcache(Oid foid, FuncExprState *fca -*** 1034,1039 **** ---- 1035,1041 ---- - aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); -+ sepgsql_proc_execute(foid); - - /* - * Safety check on nargs. Under normal circumstances this should never -*************** ExecEvalArrayCoerceExpr(ArrayCoerceExprS -*** 4032,4037 **** ---- 4034,4040 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(acoerce->elemfuncid)); -+ sepgsql_proc_execute(acoerce->elemfuncid); - - /* Set up the primary fmgr lookup information */ - fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), -diff -Nrpc base/src/backend/executor/execScan.c sepgsql/src/backend/executor/execScan.c -*** base/src/backend/executor/execScan.c Thu Apr 9 00:13:21 2009 ---- sepgsql/src/backend/executor/execScan.c Wed Sep 9 13:14:37 2009 -*************** -*** 20,25 **** ---- 20,26 ---- - - #include "executor/executor.h" - #include "miscadmin.h" -+ #include "security/rowlevel.h" - #include "utils/memutils.h" - - -*************** ExecScan(ScanState *node, -*** 53,58 **** ---- 54,60 ---- - ProjectionInfo *projInfo; - ExprDoneCond isDone; - TupleTableSlot *resultSlot; -+ Scan *scan = (Scan *)node->ps.plan; - - /* - * Fetch data from node -*************** ExecScan(ScanState *node, -*** 64,70 **** - * If we have neither a qual to check nor a projection to do, just skip - * all the overhead and return the raw scan tuple. - */ -! if (!qual && !projInfo) - return (*accessMtd) (node); - - /* ---- 66,72 ---- - * If we have neither a qual to check nor a projection to do, just skip - * all the overhead and return the raw scan tuple. - */ -! if (!qual && !projInfo && !scan->rowlvPerms) - return (*accessMtd) (node); - - /* -*************** ExecScan(ScanState *node, -*** 128,136 **** - * when the qual is nil ... saves only a few cycles, but they add up - * ... - */ -! if (!qual || ExecQual(qual, econtext, false)) - { - /* - * Found a satisfactory scan tuple. - */ - if (projInfo) ---- 130,147 ---- - * when the qual is nil ... saves only a few cycles, but they add up - * ... - */ -! if (rowlvExecScanFilter(scan, node->ss_currentRelation, slot) -! && (!qual || ExecQual(qual, econtext, false))) - { - /* -+ * NOTE: On FK checks, the Row-level feature needs to raise -+ * an error after evaluation of all the given quals to avoid -+ * incorrect error reporting. We assume FK implementation -+ * does not use malicious functions as the quals. -+ */ -+ rowlvExecScanAbort(scan, node->ss_currentRelation, slot); -+ -+ /* - * Found a satisfactory scan tuple. - */ - if (projInfo) -*************** tlist_matches_tupdesc(PlanState *ps, Lis -*** 197,202 **** ---- 208,214 ---- - int numattrs = tupdesc->natts; - int attrno; - bool hasoid; -+ bool hassecid; - ListCell *tlist_item = list_head(tlist); - - /* Check the tlist attributes */ -*************** tlist_matches_tupdesc(PlanState *ps, Lis -*** 240,251 **** - return false; /* tlist too long */ - - /* -! * If the plan context requires a particular hasoid setting, then that has -! * to match, too. - */ - if (ExecContextForcesOids(ps, &hasoid) && - hasoid != tupdesc->tdhasoid) - return false; - - return true; - } ---- 252,267 ---- - return false; /* tlist too long */ - - /* -! * If the plan context requires a particular hasoid or hassecid setting, -! * then that has to match, too. - */ - if (ExecContextForcesOids(ps, &hasoid) && - hasoid != tupdesc->tdhasoid) - return false; - -+ if (ExecContextForcesSecids(ps, &hassecid) && -+ hassecid != tupdesc->tdhassecid) -+ return false; -+ - return true; - } -diff -Nrpc base/src/backend/executor/execTuples.c sepgsql/src/backend/executor/execTuples.c -*** base/src/backend/executor/execTuples.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/executor/execTuples.c Wed Sep 9 13:14:37 2009 -*************** -*** 100,106 **** - - - static TupleDesc ExecTypeFromTLInternal(List *targetList, -! bool hasoid, bool skipjunk); - - - /* ---------------------------------------------------------------- ---- 100,106 ---- - - - static TupleDesc ExecTypeFromTLInternal(List *targetList, -! bool hasoid, bool hasseclabel, bool skipjunk); - - - /* ---------------------------------------------------------------- -*************** ExecInitNullTupleSlot(EState *estate, Tu -*** 968,976 **** - * ---------------------------------------------------------------- - */ - TupleDesc -! ExecTypeFromTL(List *targetList, bool hasoid) - { -! return ExecTypeFromTLInternal(targetList, hasoid, false); - } - - /* ---------------------------------------------------------------- ---- 968,976 ---- - * ---------------------------------------------------------------- - */ - TupleDesc -! ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid) - { -! return ExecTypeFromTLInternal(targetList, hasoid, hassecid, false); - } - - /* ---------------------------------------------------------------- -*************** ExecTypeFromTL(List *targetList, bool ha -*** 980,992 **** - * ---------------------------------------------------------------- - */ - TupleDesc -! ExecCleanTypeFromTL(List *targetList, bool hasoid) - { -! return ExecTypeFromTLInternal(targetList, hasoid, true); - } - - static TupleDesc -! ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) - { - TupleDesc typeInfo; - ListCell *l; ---- 980,993 ---- - * ---------------------------------------------------------------- - */ - TupleDesc -! ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid) - { -! return ExecTypeFromTLInternal(targetList, hasoid, hassecid, true); - } - - static TupleDesc -! ExecTypeFromTLInternal(List *targetList, bool hasoid, -! bool hassecid, bool skipjunk) - { - TupleDesc typeInfo; - ListCell *l; -*************** ExecTypeFromTLInternal(List *targetList, -*** 998,1003 **** ---- 999,1005 ---- - else - len = ExecTargetListLength(targetList); - typeInfo = CreateTemplateTupleDesc(len, hasoid); -+ typeInfo->tdhassecid = hassecid; - - foreach(l, targetList) - { -diff -Nrpc base/src/backend/executor/execUtils.c sepgsql/src/backend/executor/execUtils.c -*** base/src/backend/executor/execUtils.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/executor/execUtils.c Wed Sep 9 13:14:37 2009 -*************** void -*** 512,517 **** ---- 512,518 ---- - ExecAssignResultTypeFromTL(PlanState *planstate) - { - bool hasoid; -+ bool hassecid; - TupleDesc tupDesc; - - if (ExecContextForcesOids(planstate, &hasoid)) -*************** ExecAssignResultTypeFromTL(PlanState *pl -*** 524,535 **** - hasoid = false; - } - - /* - * ExecTypeFromTL needs the parse-time representation of the tlist, not a - * list of ExprStates. This is good because some plan nodes don't bother - * to set up planstate->targetlist ... - */ -! tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid); - ExecAssignResultType(planstate, tupDesc); - } - ---- 525,539 ---- - hasoid = false; - } - -+ if (!ExecContextForcesSecids(planstate, &hassecid)) -+ hassecid = false; -+ - /* - * ExecTypeFromTL needs the parse-time representation of the tlist, not a - * list of ExprStates. This is good because some plan nodes don't bother - * to set up planstate->targetlist ... - */ -! tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid, hassecid); - ExecAssignResultType(planstate, tupDesc); - } - -diff -Nrpc base/src/backend/executor/functions.c sepgsql/src/backend/executor/functions.c -*** base/src/backend/executor/functions.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/executor/functions.c Wed Jul 15 19:30:50 2009 -*************** check_sql_fn_retval(Oid func_id, Oid ret -*** 1135,1141 **** - - /* Set up junk filter if needed */ - if (junkFilter) -! *junkFilter = ExecInitJunkFilter(tlist, false, NULL); - } - else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) - { ---- 1135,1141 ---- - - /* Set up junk filter if needed */ - if (junkFilter) -! *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); - } - else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) - { -*************** check_sql_fn_retval(Oid func_id, Oid ret -*** 1167,1173 **** - COERCE_DONTCARE); - /* Set up junk filter if needed */ - if (junkFilter) -! *junkFilter = ExecInitJunkFilter(tlist, false, NULL); - return false; /* NOT returning whole tuple */ - } - } ---- 1167,1173 ---- - COERCE_DONTCARE); - /* Set up junk filter if needed */ - if (junkFilter) -! *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); - return false; /* NOT returning whole tuple */ - } - } -*************** check_sql_fn_retval(Oid func_id, Oid ret -*** 1180,1186 **** - * what the caller expects will happen at runtime. - */ - if (junkFilter) -! *junkFilter = ExecInitJunkFilter(tlist, false, NULL); - return true; - } - Assert(tupdesc); ---- 1180,1186 ---- - * what the caller expects will happen at runtime. - */ - if (junkFilter) -! *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); - return true; - } - Assert(tupdesc); -diff -Nrpc base/src/backend/executor/nodeAgg.c sepgsql/src/backend/executor/nodeAgg.c -*** base/src/backend/executor/nodeAgg.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/executor/nodeAgg.c Thu Sep 17 17:04:16 2009 -*************** -*** 81,86 **** ---- 81,87 ---- - #include "parser/parse_agg.h" - #include "parser/parse_coerce.h" - #include "parser/parse_oper.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -*************** ExecInitAgg(Agg *node, EState *estate, i -*** 1431,1436 **** ---- 1432,1438 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(aggref->aggfnoid)); -+ sepgsql_proc_execute(aggref->aggfnoid); - - peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; - peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; -*************** ExecInitAgg(Agg *node, EState *estate, i -*** 1454,1459 **** ---- 1456,1462 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(transfn_oid)); -+ sepgsql_proc_execute(transfn_oid); - if (OidIsValid(finalfn_oid)) - { - aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, -*************** ExecInitAgg(Agg *node, EState *estate, i -*** 1461,1466 **** ---- 1464,1470 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(finalfn_oid)); -+ sepgsql_proc_execute(finalfn_oid); - } - } - -diff -Nrpc base/src/backend/executor/nodeMergejoin.c sepgsql/src/backend/executor/nodeMergejoin.c -*** base/src/backend/executor/nodeMergejoin.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/executor/nodeMergejoin.c Thu Sep 17 17:04:16 2009 -*************** -*** 98,103 **** ---- 98,104 ---- - #include "executor/execdefs.h" - #include "executor/nodeMergejoin.h" - #include "miscadmin.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/lsyscache.h" - #include "utils/memutils.h" -*************** MJExamineQuals(List *mergeclauses, -*** 215,220 **** ---- 216,222 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(cmpproc)); -+ sepgsql_proc_execute(cmpproc); - - /* Set up the fmgr lookup information */ - fmgr_info(cmpproc, &(clause->cmpfinfo)); -diff -Nrpc base/src/backend/executor/nodeSubplan.c sepgsql/src/backend/executor/nodeSubplan.c -*** base/src/backend/executor/nodeSubplan.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/executor/nodeSubplan.c Wed Jul 15 19:30:50 2009 -*************** ExecInitSubPlan(SubPlan *subplan, PlanSt -*** 869,875 **** - * (hack alert!). The righthand expressions will be evaluated in our - * own innerecontext. - */ -! tupDesc = ExecTypeFromTL(leftptlist, false); - slot = ExecAllocTableSlot(tupTable); - ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, ---- 869,875 ---- - * (hack alert!). The righthand expressions will be evaluated in our - * own innerecontext. - */ -! tupDesc = ExecTypeFromTL(leftptlist, false, false); - slot = ExecAllocTableSlot(tupTable); - ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, -*************** ExecInitSubPlan(SubPlan *subplan, PlanSt -*** 877,883 **** - slot, - NULL); - -! tupDesc = ExecTypeFromTL(rightptlist, false); - slot = ExecAllocTableSlot(tupTable); - ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, ---- 877,883 ---- - slot, - NULL); - -! tupDesc = ExecTypeFromTL(rightptlist, false, false); - slot = ExecAllocTableSlot(tupTable); - ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, -diff -Nrpc base/src/backend/executor/nodeWindowAgg.c sepgsql/src/backend/executor/nodeWindowAgg.c -*** base/src/backend/executor/nodeWindowAgg.c Tue Jun 23 11:09:47 2009 ---- sepgsql/src/backend/executor/nodeWindowAgg.c Thu Sep 17 17:04:16 2009 -*************** -*** 43,48 **** ---- 43,49 ---- - #include "optimizer/clauses.h" - #include "parser/parse_agg.h" - #include "parser/parse_coerce.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/datum.h" -*************** ExecInitWindowAgg(WindowAgg *node, EStat -*** 1224,1229 **** ---- 1225,1231 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(wfunc->winfnoid)); -+ sepgsql_proc_execute(wfunc->winfnoid); - - /* Fill in the perfuncstate data */ - perfuncstate->wfuncstate = wfuncstate; -*************** initialize_peragg(WindowAggState *winsta -*** 1418,1423 **** ---- 1420,1426 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(transfn_oid)); -+ sepgsql_proc_execute(transfn_oid); - if (OidIsValid(finalfn_oid)) - { - aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, -*************** initialize_peragg(WindowAggState *winsta -*** 1425,1430 **** ---- 1428,1434 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(finalfn_oid)); -+ sepgsql_proc_execute(finalfn_oid); - } - } - -diff -Nrpc base/src/backend/executor/spi.c sepgsql/src/backend/executor/spi.c -*** base/src/backend/executor/spi.c 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 base/src/backend/libpq/be-fsstubs.c sepgsql/src/backend/libpq/be-fsstubs.c -*** base/src/backend/libpq/be-fsstubs.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/libpq/be-fsstubs.c Mon Sep 28 10:07:19 2009 -*************** -*** 42,50 **** ---- 42,53 ---- - #include - #include - -+ #include "catalog/pg_largeobject.h" -+ #include "catalog/pg_security.h" - #include "libpq/be-fsstubs.h" - #include "libpq/libpq-fs.h" - #include "miscadmin.h" -+ #include "security/sepgsql.h" - #include "storage/fd.h" - #include "storage/large_object.h" - #include "utils/builtins.h" -*************** lo_read(int fd, char *buf, int len) -*** 156,161 **** ---- 159,167 ---- - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("invalid large-object descriptor: %d", fd))); - -+ /* SELinux checks db_blob:{read} */ -+ sepgsqlCheckBlobRead(cookies[fd]); -+ - status = inv_read(cookies[fd], buf, len); - - return status; -*************** lo_write(int fd, const char *buf, int le -*** 177,182 **** ---- 183,191 ---- - errmsg("large object descriptor %d was not opened for writing", - fd))); - -+ /* SELinux checks db_blob:{write} */ -+ sepgsqlCheckBlobWrite(cookies[fd]); -+ - status = inv_write(cookies[fd], buf, len); - - return status; -*************** lo_import_internal(text *filename, Oid l -*** 364,369 **** ---- 373,381 ---- - * open the file to be read in - */ - text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); -+ /* SELinux checks db_blob:{write import} and file:{read} */ -+ //sepgsqlCheckBlobImport(lobj, fnamebuf); -+ - fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666); - if (fd < 0) - ereport(ERROR, -*************** lo_export(PG_FUNCTION_ARGS) -*** 439,444 **** ---- 451,459 ---- - * world-writable export files doesn't seem wise. - */ - text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); -+ /* SELinux checks db_blob:{read export} and file:{write} */ -+ //sepgsqlCheckBlobExport(lobj, fnamebuf); -+ - oumask = umask((mode_t) 0022); - fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666); - umask(oumask); -*************** lo_export(PG_FUNCTION_ARGS) -*** 447,453 **** - (errcode_for_file_access(), - errmsg("could not create server file \"%s\": %m", - fnamebuf))); -- - /* - * read in from the inversion file and write to the filesystem - */ ---- 462,467 ---- -*************** lo_truncate(PG_FUNCTION_ARGS) -*** 482,493 **** ---- 496,559 ---- - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("invalid large-object descriptor: %d", fd))); - -+ /* SELinux checks db_blob:{write} */ -+ sepgsqlCheckBlobWrite(cookies[fd]); -+ - inv_truncate(cookies[fd], len); - - PG_RETURN_INT32(0); - } - - /* -+ * lo_get_seclabel -+ * get a security label of large object -+ */ -+ Datum -+ lo_get_security(PG_FUNCTION_ARGS) -+ { -+ Oid loid = PG_GETARG_OID(0); -+ Oid secid; -+ char *seclabel; -+ -+ secid = inv_get_security(loid); -+ seclabel = securityTransSecLabelOut(LargeObjectRelationId, secid); -+ -+ return CStringGetTextDatum(seclabel); -+ } -+ -+ /* -+ * lo_set_seclabel -+ * set a security label of large object -+ */ -+ Datum -+ lo_set_security(PG_FUNCTION_ARGS) -+ { -+ Oid loid = PG_GETARG_OID(0); -+ char *seclabel = TextDatumGetCString(PG_GETARG_DATUM(1)); -+ Oid secid; -+ -+ secid = securityTransSecLabelIn(LargeObjectRelationId, seclabel); -+ -+ inv_set_security(loid, secid); -+ -+ /* -+ * Also on memory caches to be updated -+ */ -+ if (fscxt != NULL) -+ { -+ int i; -+ -+ for (i = 0; i < cookies_size; i++) -+ { -+ if (cookies[i] != NULL && cookies[i]->id == loid) -+ cookies[i]->secid = secid; -+ } -+ } -+ -+ PG_RETURN_BOOL(true); -+ } -+ -+ /* - * AtEOXact_LargeObject - - * prepares large objects for transaction commit - */ -diff -Nrpc base/src/backend/nodes/copyfuncs.c sepgsql/src/backend/nodes/copyfuncs.c -*** base/src/backend/nodes/copyfuncs.c 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 base/src/backend/nodes/equalfuncs.c sepgsql/src/backend/nodes/equalfuncs.c -*** base/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 base/src/backend/nodes/outfuncs.c sepgsql/src/backend/nodes/outfuncs.c -*** base/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 base/src/backend/optimizer/plan/createplan.c sepgsql/src/backend/optimizer/plan/createplan.c -*** base/src/backend/optimizer/plan/createplan.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/optimizer/plan/createplan.c Sun Sep 6 19:53:10 2009 -*************** create_scan_plan(PlannerInfo *root, Path -*** 305,310 **** ---- 305,313 ---- - break; - } - -+ /* Copy of row-level permissions to Scan node */ -+ ((Scan *)plan)->rowlvPerms = rel->rowlvPerms; -+ - /* - * If there are any pseudoconstant clauses attached to this node, insert a - * gating Result node that evaluates the pseudoconstants as one-time -diff -Nrpc base/src/backend/optimizer/util/clauses.c sepgsql/src/backend/optimizer/util/clauses.c -*** base/src/backend/optimizer/util/clauses.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/optimizer/util/clauses.c Thu Sep 17 17:04:16 2009 -*************** -*** 38,43 **** ---- 38,44 ---- - #include "parser/parse_coerce.h" - #include "parser/parse_func.h" - #include "rewrite/rewriteManip.h" -+ #include "security/sepgsql.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" - #include "utils/builtins.h" -*************** inline_function(Oid funcid, Oid result_t -*** 3502,3507 **** ---- 3503,3509 ---- - funcform->prosecdef || - funcform->proretset || - !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || -+ !sepgsql_proc_hint_inlined(func_tuple) || - funcform->pronargs != list_length(args)) - return NULL; - -*************** inline_set_returning_function(PlannerInf -*** 3970,3975 **** ---- 3972,3978 ---- - funcform->prosecdef || - !funcform->proretset || - !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || -+ !sepgsql_proc_hint_inlined(func_tuple) || - funcform->pronargs != list_length(fexpr->args)) - { - ReleaseSysCache(func_tuple); -diff -Nrpc base/src/backend/optimizer/util/relnode.c sepgsql/src/backend/optimizer/util/relnode.c -*** base/src/backend/optimizer/util/relnode.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/optimizer/util/relnode.c Wed Jul 15 19:39:56 2009 -*************** -*** 21,26 **** ---- 21,27 ---- - #include "optimizer/plancat.h" - #include "optimizer/restrictinfo.h" - #include "parser/parsetree.h" -+ #include "security/rowlevel.h" - #include "utils/hsearch.h" - - -*************** build_simple_rel(PlannerInfo *root, int -*** 91,96 **** ---- 92,98 ---- - rel->has_eclass_joins = false; - rel->index_outer_relids = NULL; - rel->index_inner_paths = NIL; -+ rel->rowlvPerms = rowlvSetupPermissions(rte); - - /* Check type of rtable entry */ - switch (rte->rtekind) -diff -Nrpc base/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze.c -*** base/src/backend/parser/analyze.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/backend/parser/analyze.c Tue Dec 15 17:30:25 2009 -*************** -*** 25,30 **** ---- 25,31 ---- - #include "postgres.h" - - #include "access/sysattr.h" -+ #include "catalog/heap.h" - #include "catalog/pg_type.h" - #include "nodes/makefuncs.h" - #include "nodes/nodeFuncs.h" -*************** transformInsertStmt(ParseState *pstate, -*** 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 base/src/backend/parser/gram.y sepgsql/src/backend/parser/gram.y -*** base/src/backend/parser/gram.y Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/parser/gram.y Tue Sep 8 17:13:41 2009 -*************** -*** 58,63 **** ---- 58,64 ---- - #include "nodes/makefuncs.h" - #include "nodes/nodeFuncs.h" - #include "parser/gramparse.h" -+ #include "security/sepgsql.h" - #include "storage/lmgr.h" - #include "utils/date.h" - #include "utils/datetime.h" -*************** static TypeName *TableFuncTypeName(List -*** 184,190 **** - %type stmt schema_stmt - AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt - AlterForeignServerStmt AlterGroupStmt -! AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt - AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt - AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt - ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt ---- 185,191 ---- - %type stmt schema_stmt - AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt - AlterForeignServerStmt AlterGroupStmt -! AlterObjectSchemaStmt AlterOwnerStmt AlterSecLabelStmt AlterSeqStmt AlterTableStmt - AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt - AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt - ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt -*************** static TypeName *TableFuncTypeName(List -*** 401,406 **** ---- 402,409 ---- - %type OptTableSpace OptConsTableSpace OptTableSpaceOwner - %type opt_check_option - -+ %type OptSecCxt SecCxtItem -+ - %type xml_attribute_el - %type xml_attribute_list xml_attributes - %type xml_root_version opt_xml_root_standalone -*************** stmt : -*** 607,612 **** ---- 610,616 ---- - | AlterGroupStmt - | AlterObjectSchemaStmt - | AlterOwnerStmt -+ | AlterSecLabelStmt - | AlterSeqStmt - | AlterTableStmt - | AlterRoleSetStmt -*************** DropGroupStmt: -*** 1041,1047 **** - *****************************************************************************/ - - CreateSchemaStmt: -! CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSchemaEltList - { - CreateSchemaStmt *n = makeNode(CreateSchemaStmt); - /* One can omit the schema name or the authorization id. */ ---- 1045,1051 ---- - *****************************************************************************/ - - CreateSchemaStmt: -! CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSecCxt OptSchemaEltList - { - CreateSchemaStmt *n = makeNode(CreateSchemaStmt); - /* One can omit the schema name or the authorization id. */ -*************** CreateSchemaStmt: -*** 1050,1065 **** - else - n->schemaname = $5; - n->authid = $5; -! n->schemaElts = $6; - $$ = (Node *)n; - } -! | CREATE SCHEMA ColId OptSchemaEltList - { - CreateSchemaStmt *n = makeNode(CreateSchemaStmt); - /* ...but not both */ - n->schemaname = $3; - n->authid = NULL; -! n->schemaElts = $4; - $$ = (Node *)n; - } - ; ---- 1054,1071 ---- - else - n->schemaname = $5; - n->authid = $5; -! n->secLabel = (Node *)$6; -! n->schemaElts = $7; - $$ = (Node *)n; - } -! | CREATE SCHEMA ColId OptSecCxt OptSchemaEltList - { - CreateSchemaStmt *n = makeNode(CreateSchemaStmt); - /* ...but not both */ - n->schemaname = $3; - n->authid = NULL; -! n->secLabel = (Node *)$4; -! n->schemaElts = $5; - $$ = (Node *)n; - } - ; -*************** opt_using: -*** 2036,2042 **** - *****************************************************************************/ - - CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' -! OptInherit OptWith OnCommitOption OptTableSpace - { - CreateStmt *n = makeNode(CreateStmt); - $4->istemp = $2; ---- 2042,2048 ---- - *****************************************************************************/ - - CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' -! OptInherit OptWith OnCommitOption OptTableSpace OptSecCxt - { - CreateStmt *n = makeNode(CreateStmt); - $4->istemp = $2; -*************** CreateStmt: CREATE OptTemp TABLE qualifi -*** 2047,2056 **** - n->options = $9; - n->oncommit = $10; - n->tablespacename = $11; - $$ = (Node *)n; - } - | CREATE OptTemp TABLE qualified_name OF qualified_name -! '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace - { - /* SQL99 CREATE TABLE OF (cols) seems to be satisfied - * by our inheritance capabilities. Let's try it... ---- 2053,2063 ---- - n->options = $9; - n->oncommit = $10; - n->tablespacename = $11; -+ n->secLabel = (Node *) $12; - $$ = (Node *)n; - } - | CREATE OptTemp TABLE qualified_name OF qualified_name -! '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace OptSecCxt - { - /* SQL99 CREATE TABLE OF (cols) seems to be satisfied - * by our inheritance capabilities. Let's try it... -*************** CreateStmt: CREATE OptTemp TABLE qualifi -*** 2064,2069 **** ---- 2071,2077 ---- - n->options = $10; - n->oncommit = $11; - n->tablespacename = $12; -+ n->secLabel = (Node *) $13; - $$ = (Node *)n; - } - ; -*************** TableElement: -*** 2106,2118 **** - | TableConstraint { $$ = $1; } - ; - -! columnDef: ColId Typename ColQualList - { - ColumnDef *n = makeNode(ColumnDef); - n->colname = $1; - n->typename = $2; - n->constraints = $3; - n->is_local = true; - $$ = (Node *)n; - } - ; ---- 2114,2127 ---- - | TableConstraint { $$ = $1; } - ; - -! columnDef: ColId Typename ColQualList OptSecCxt - { - ColumnDef *n = makeNode(ColumnDef); - n->colname = $1; - n->typename = $2; - n->constraints = $3; - n->is_local = true; -+ n->secLabel = (Node *) $4; - $$ = (Node *)n; - } - ; -*************** opt_with_data: -*** 2584,2595 **** - *****************************************************************************/ - - CreateSeqStmt: -! CREATE OptTemp SEQUENCE qualified_name OptSeqOptList - { - CreateSeqStmt *n = makeNode(CreateSeqStmt); - $4->istemp = $2; - n->sequence = $4; - n->options = $5; - $$ = (Node *)n; - } - ; ---- 2593,2605 ---- - *****************************************************************************/ - - CreateSeqStmt: -! CREATE OptTemp SEQUENCE qualified_name OptSeqOptList OptSecCxt - { - CreateSeqStmt *n = makeNode(CreateSeqStmt); - $4->istemp = $2; - n->sequence = $4; - n->options = $5; -+ n->secLabel = (Node *)$6; - $$ = (Node *)n; - } - ; -*************** createfunc_opt_item: -*** 4885,4890 **** ---- 4895,4904 ---- - { - $$ = makeDefElem("window", (Node *)makeInteger(TRUE)); - } -+ | SecCxtItem -+ { -+ $$ = $1; -+ } - | common_func_opt_item - { - $$ = $1; -*************** AlterOwnerStmt: ALTER AGGREGATE func_nam -*** 5591,5596 **** ---- 5605,5680 ---- - } - ; - -+ /***************************************************************************** -+ * -+ * ALTER THING name SECURITY_CONTEXT [=] -+ * -+ *****************************************************************************/ -+ -+ AlterSecLabelStmt: ALTER DATABASE database_name SecCxtItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_DATABASE; -+ n->object = list_make1(makeString($3)); -+ n->secLabel = (Node *)$4; -+ $$ = (Node *) n; -+ } -+ | ALTER SCHEMA name SecCxtItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_SCHEMA; -+ n->object = list_make1(makeString($3)); -+ n->secLabel = (Node *)$4; -+ $$ = (Node *) n; -+ } -+ | ALTER TABLE relation_expr SecCxtItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_TABLE; -+ n->relation = $3; -+ n->secLabel = (Node *)$4; -+ $$ = (Node *) n; -+ } -+ | ALTER TABLE relation_expr ALTER opt_column ColId SecCxtItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_COLUMN; -+ n->relation = $3; -+ n->subname = $6; -+ n->secLabel = (Node *)$7; -+ $$ = (Node *) n; -+ } -+ | ALTER SEQUENCE relation_expr SecCxtItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_SEQUENCE; -+ n->relation = $3; -+ n->secLabel = (Node *)$4; -+ $$ = (Node *) n; -+ } -+ | ALTER FUNCTION function_with_argtypes SecCxtItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_FUNCTION; -+ n->object = $3->funcname; -+ n->objarg = $3->funcargs; -+ n->secLabel = (Node *)$4; -+ $$ = (Node *) n; -+ } -+ ; -+ -+ OptSecCxt: SecCxtItem { $$ = $1; } -+ | /* EMPTY */ { $$ = NULL; } -+ ; -+ -+ SecCxtItem: IDENT opt_equal Sconst -+ { -+ if (strcmp("security_context", $1) != 0) -+ yyerror("syntax error"); -+ $$ = makeDefElem($1, (Node *) makeString($3)); -+ } -+ ; -+ - - /***************************************************************************** - * -*************** createdb_opt_item: -*** 6033,6038 **** ---- 6117,6126 ---- - { - $$ = makeDefElem("owner", NULL); - } -+ | SecCxtItem -+ { -+ $$ = $1; -+ } - ; - - /* -diff -Nrpc base/src/backend/parser/parse_target.c sepgsql/src/backend/parser/parse_target.c -*** base/src/backend/parser/parse_target.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/parser/parse_target.c Wed Jul 15 19:38:52 2009 -*************** -*** 14,19 **** ---- 14,20 ---- - */ - #include "postgres.h" - -+ #include "catalog/heap.h" - #include "catalog/pg_type.h" - #include "commands/dbcommands.h" - #include "funcapi.h" -*************** transformAssignedExpr(ParseState *pstate -*** 361,376 **** - Oid attrtype; /* type of target column */ - int32 attrtypmod; - Relation rd = pstate->p_target_relation; - - Assert(rd != NULL); -! if (attrno <= 0) -! ereport(ERROR, -! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -! errmsg("cannot assign to system column \"%s\"", -! colname), -! parser_errposition(pstate, location))); -! attrtype = attnumTypeId(rd, attrno); -! attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; - - /* - * If the expression is a DEFAULT placeholder, insert the attribute's ---- 362,394 ---- - Oid attrtype; /* type of target column */ - int32 attrtypmod; - Relation rd = pstate->p_target_relation; -+ bool relhasoids = RelationGetForm(rd)->relhasoids; - - Assert(rd != NULL); -! if (attrno > 0) -! { -! attrtype = attnumTypeId(rd, attrno); -! attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; -! } -! else -! { -! Form_pg_attribute attForm -! = SystemAttributeDefinition(attrno, relhasoids); -! if (attForm && SystemAttributeIsWritable(attrno)) -! { -! attrtype = attForm->atttypid; -! attrtypmod = attForm->atttypmod; -! } -! else -! { -! ereport(ERROR, -! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -! errmsg("cannot assign to system column \"%s\"", -! colname), -! parser_errposition(pstate, location))); -! return NULL; /* compiler kindness */ -! } -! } - - /* - * If the expression is a DEFAULT placeholder, insert the attribute's -*************** updateTargetListEntry(ParseState *pstate -*** 515,520 **** ---- 533,541 ---- - */ - tle->resno = (AttrNumber) attrno; - tle->resname = colname; -+ -+ if (SystemAttributeIsWritable(attrno)) -+ tle->resjunk = true; - } - - -*************** checkInsertTargets(ParseState *pstate, L -*** 789,794 **** ---- 810,816 ---- - Bitmapset *wholecols = NULL; - Bitmapset *partialcols = NULL; - ListCell *tl; -+ uint32 system_attrs = 0UL; - - foreach(tl, cols) - { -*************** checkInsertTargets(ParseState *pstate, L -*** 797,810 **** - int attrno; - - /* Lookup column name, ereport on failure */ -! attrno = attnameAttNum(pstate->p_target_relation, name, false); - if (attrno == InvalidAttrNumber) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - name, - RelationGetRelationName(pstate->p_target_relation)), - parser_errposition(pstate, col->location))); - - /* - * Check for duplicates, but only of whole columns --- we allow ---- 819,855 ---- - int attrno; - - /* Lookup column name, ereport on failure */ -! attrno = attnameAttNum(pstate->p_target_relation, name, true); - if (attrno == InvalidAttrNumber) -+ { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - name, - RelationGetRelationName(pstate->p_target_relation)), - parser_errposition(pstate, col->location))); -+ } -+ else if (attrno < 0) -+ { -+ if (SystemAttributeIsWritable(attrno)) -+ { -+ uint32 mask = (1<<(-attrno)); -+ -+ if ((system_attrs & mask) != 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_DUPLICATE_COLUMN), -+ errmsg("column \"%s\" specified more than once", name), -+ parser_errposition(pstate, col->location))); -+ system_attrs |= mask; -+ *attrnos = lappend_int(*attrnos, attrno); -+ continue; -+ } -+ ereport(ERROR, -+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), -+ errmsg("column \"%s\" of relation \"%s\" is system column", -+ name, RelationGetRelationName(pstate->p_target_relation)), -+ parser_errposition(pstate, col->location))); -+ } - - /* - * Check for duplicates, but only of whole columns --- we allow -diff -Nrpc base/src/backend/parser/parse_utilcmd.c sepgsql/src/backend/parser/parse_utilcmd.c -*** base/src/backend/parser/parse_utilcmd.c 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 base/src/backend/postmaster/autovacuum.c sepgsql/src/backend/postmaster/autovacuum.c -*** base/src/backend/postmaster/autovacuum.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/postmaster/autovacuum.c Sun Sep 6 19:53:10 2009 -*************** do_autovacuum(void) -*** 2004,2010 **** - object.classId = RelationRelationId; - object.objectId = relid; - object.objectSubId = 0; -! performDeletion(&object, DROP_CASCADE); - } - else - { ---- 2004,2010 ---- - object.classId = RelationRelationId; - object.objectId = relid; - object.objectSubId = 0; -! performDeletionNoPerms(&object, DROP_CASCADE); - } - else - { -diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmaster/postmaster.c -*** base/src/backend/postmaster/postmaster.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/backend/postmaster/postmaster.c Tue Dec 15 17:30:25 2009 -*************** -*** 108,113 **** ---- 108,114 ---- - #include "postmaster/pgarch.h" - #include "postmaster/postmaster.h" - #include "postmaster/syslogger.h" -+ #include "security/sepgsql.h" - #include "storage/fd.h" - #include "storage/ipc.h" - #include "storage/pg_shmem.h" -*************** static pid_t StartupPID = 0, -*** 209,215 **** - AutoVacPID = 0, - PgArchPID = 0, - PgStatPID = 0, -! SysLoggerPID = 0; - - /* Startup/shutdown state */ - #define NoShutdown 0 ---- 210,217 ---- - AutoVacPID = 0, - PgArchPID = 0, - PgStatPID = 0, -! SysLoggerPID = 0, -! sepgsqlReceiverPID = 0; - - /* Startup/shutdown state */ - #define NoShutdown 0 -*************** static void ShmemBackendArrayRemove(Back -*** 445,450 **** ---- 447,453 ---- - #define StartupDataBase() StartChildProcess(StartupProcess) - #define StartBackgroundWriter() StartChildProcess(BgWriterProcess) - #define StartWalWriter() StartChildProcess(WalWriterProcess) -+ #define StartSELinuxReceiver() StartChildProcess(SelinuxReceiverProcess) - - /* Macros to check exit status of a child process */ - #define EXIT_STATUS_0(st) ((st) == 0) -*************** ServerLoop(void) -*** 1436,1441 **** ---- 1439,1449 ---- - if (PgStatPID == 0 && pmState == PM_RUN) - PgStatPID = pgstat_start(); - -+ /* if we have lost the selinux netlink receiver, try to start */ -+ if (sepgsqlIsEnabled() && -+ sepgsqlReceiverPID == 0 && pmState == PM_RUN) -+ sepgsqlReceiverPID = StartSELinuxReceiver(); -+ - /* If we need to signal the autovacuum launcher, do so now */ - if (avlauncher_needs_signal) - { -*************** SIGHUP_handler(SIGNAL_ARGS) -*** 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 base/src/backend/rewrite/rewriteDefine.c sepgsql/src/backend/rewrite/rewriteDefine.c -*** base/src/backend/rewrite/rewriteDefine.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/rewrite/rewriteDefine.c Fri Sep 18 14:51:00 2009 -*************** -*** 27,32 **** ---- 27,33 ---- - #include "rewrite/rewriteDefine.h" - #include "rewrite/rewriteManip.h" - #include "rewrite/rewriteSupport.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/inval.h" -*************** DefineQueryRewrite(char *rulename, -*** 266,271 **** ---- 267,275 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(event_relation)); - -+ /* SELinux checks */ -+ sepgsql_rule_create(event_relid, rulename); -+ - /* - * No rule actions that modify OLD or NEW - */ -diff -Nrpc base/src/backend/rewrite/rewriteRemove.c sepgsql/src/backend/rewrite/rewriteRemove.c -*** base/src/backend/rewrite/rewriteRemove.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/rewrite/rewriteRemove.c Fri Sep 18 14:51:00 2009 -*************** -*** 22,27 **** ---- 22,28 ---- - #include "catalog/pg_rewrite.h" - #include "miscadmin.h" - #include "rewrite/rewriteRemove.h" -+ #include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/fmgroids.h" - #include "utils/inval.h" -*************** RemoveRewriteRule(Oid owningRel, const c -*** 78,83 **** ---- 79,87 ---- - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - get_rel_name(eventRelationOid)); - -+ /* SELinux checks */ -+ sepgsql_rule_drop(eventRelationOid, ruleName); -+ - /* - * Do the deletion - */ -diff -Nrpc base/src/backend/security/Makefile sepgsql/src/backend/security/Makefile -*** base/src/backend/security/Makefile Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/Makefile Wed Jul 15 19:39:56 2009 -*************** -*** 0 **** ---- 1,13 ---- -+ # -+ # Makefile for the enhanced security subsystem -+ # -+ -+ subdir = src/backend/security -+ top_builddir = ../../.. -+ include $(top_builddir)/src/Makefile.global -+ -+ SUBDIRS = sepgsql -+ -+ OBJS = rowlevel.o -+ -+ include $(top_srcdir)/src/backend/common.mk -diff -Nrpc base/src/backend/security/rowlevel.c sepgsql/src/backend/security/rowlevel.c -*** base/src/backend/security/rowlevel.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/rowlevel.c Thu Jul 16 17:22:29 2009 -*************** -*** 0 **** ---- 1,121 ---- -+ /* -+ * src/backend/security/common.c -+ * common facilities for row-level access controls both of DAC and MAC -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+ #include "postgres.h" -+ -+ #include "catalog/pg_security.h" -+ #include "security/rowlevel.h" -+ #include "security/sepgsql.h" -+ #include "storage/bufmgr.h" -+ #include "storage/bufpage.h" -+ #include "utils/rel.h" -+ #include "utils/tqual.h" -+ -+ /* -+ * rowlvGetPerformingMode -+ * rowlvSetPerformingMode -+ * enables to control the behavior of row-level features -+ * when violated tuples are detected. -+ * The default is ROWLV_FILTER_MODE which filters out -+ * violated tuples from result set, ROWLV_ABORT_MODE -+ * raises an error and ROWLV_BYPASS_MODE do nothing. -+ */ -+ static int rowlv_mode = ROWLV_FILTER_MODE; -+ -+ int rowlvGetPerformingMode(void) -+ { -+ return rowlv_mode; -+ } -+ -+ int rowlvSetPerformingMode(int new_mode) -+ { -+ int old_mode = new_mode; -+ -+ rowlv_mode = new_mode; -+ -+ return old_mode; -+ } -+ -+ /* -+ * rowlvSetupPermissions -+ * setups permissions for row-level access controls. -+ */ -+ uint32 -+ rowlvSetupPermissions(RangeTblEntry *rte) -+ { -+ return sepgsqlSetupTuplePerms(rte); -+ } -+ -+ /* -+ * rowlvExecScan -+ * a hook to filter out invisible/untouchable tuples. -+ */ -+ static bool -+ rowlvExecScan(Scan *scan, Relation rel, TupleTableSlot *slot, bool abort) -+ { -+ HeapTuple tuple; -+ uint32 perms = scan->rowlvPerms; -+ -+ if (!perms) -+ return true; -+ -+ tuple = ExecMaterializeSlot(slot); -+ -+ return sepgsqlExecScan(rel, tuple, perms, abort); -+ } -+ -+ bool -+ rowlvExecScanFilter(Scan *scan, Relation rel, TupleTableSlot *slot) -+ { -+ if (!rel || !scan->rowlvPerms || rowlv_mode != ROWLV_FILTER_MODE) -+ return true; -+ -+ return rowlvExecScan(scan, rel, slot, false); -+ } -+ -+ void -+ rowlvExecScanAbort(Scan *scan, Relation rel, TupleTableSlot *slot) -+ { -+ if (!rel || !scan->rowlvPerms || rowlv_mode != ROWLV_ABORT_MODE) -+ return; -+ -+ rowlvExecScan(scan, rel, slot, true); -+ } -+ -+ /* -+ * rowlvCopyToTuple -+ * checks permission on fetched tuple -+ */ -+ bool -+ rowlvCopyToTuple(Relation rel, HeapTuple tuple) -+ { -+ if (!sepgsqlExecScan(rel, tuple, SEPG_DB_TUPLE__SELECT, false)) -+ return false; -+ -+ return true; -+ } -+ -+ /* -+ * rowlvHeapTupleInsert -+ * assign default security attribute, and check permission -+ * if necessary. -+ */ -+ void -+ rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal) -+ { -+ sepgsqlHeapTupleInsert(rel, newtup, internal); -+ } -+ -+ /* -+ * rowlvHeapTupleUpdate -+ * check permission to change security attribute, if necesary -+ */ -+ void -+ rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup) -+ { -+ sepgsqlHeapTupleUpdate(rel, otid, newtup); -+ } -diff -Nrpc base/src/backend/security/sepgsql/Makefile sepgsql/src/backend/security/sepgsql/Makefile -*** base/src/backend/security/sepgsql/Makefile Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/Makefile Thu Sep 17 17:04:16 2009 -*************** -*** 0 **** ---- 1,16 ---- -+ # -+ # Makefile -+ # Makefile for utils/sepgsql : SE-PostgreSQL -+ # -+ -+ subdir = src/backend/security/sepgsql -+ top_builddir = ../../../.. -+ include $(top_builddir)/src/Makefile.global -+ -+ ifeq ($(enable_selinux), yes) -+ OBJS = avc.o checker.o bridge.o hooks.o label.o misc.o perms.o -+ else -+ OBJS = dummy.o -+ endif -+ -+ include $(top_srcdir)/src/backend/common.mk -diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/sepgsql/avc.c -*** base/src/backend/security/sepgsql/avc.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/avc.c 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 base/src/backend/security/sepgsql/bridge.c sepgsql/src/backend/security/sepgsql/bridge.c -*** base/src/backend/security/sepgsql/bridge.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/bridge.c Tue Dec 1 17:11:40 2009 -*************** -*** 0 **** ---- 1,2876 ---- -+ /* -+ * src/backend/security/sepgsql/bridge.c -+ * -+ * New style security hooks for SE-PostgreSQL -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+ #include "postgres.h" -+ -+ #include "access/sysattr.h" -+ #include "catalog/heap.h" -+ #include "catalog/indexing.h" -+ #include "catalog/pg_authid.h" -+ #include "catalog/pg_cast.h" -+ #include "catalog/pg_conversion.h" -+ #include "catalog/pg_database.h" -+ #include "catalog/pg_foreign_data_wrapper.h" -+ #include "catalog/pg_foreign_server.h" -+ #include "catalog/pg_language.h" -+ #include "catalog/pg_namespace.h" -+ #include "catalog/pg_operator.h" -+ #include "catalog/pg_opclass.h" -+ #include "catalog/pg_opfamily.h" -+ #include "catalog/pg_proc.h" -+ #include "catalog/pg_rewrite.h" -+ #include "catalog/pg_security.h" -+ #include "catalog/pg_tablespace.h" -+ #include "catalog/pg_ts_parser.h" -+ #include "catalog/pg_ts_dict.h" -+ #include "catalog/pg_ts_template.h" -+ #include "catalog/pg_ts_config.h" -+ #include "catalog/pg_type.h" -+ #include "catalog/pg_user_mapping.h" -+ #include "commands/dbcommands.h" -+ #include "miscadmin.h" -+ #include "security/sepgsql.h" -+ #include "utils/builtins.h" -+ #include "utils/fmgroids.h" -+ #include "utils/lsyscache.h" -+ #include "utils/syscache.h" -+ #include "utils/tqual.h" -+ -+ #include -+ -+ /* ------------------------------------------------------------ * -+ * Common Helper Routines -+ * ------------------------------------------------------------ */ -+ static bool sepgsql_database_common(Oid datOid, uint32 required, bool abort); -+ static bool sepgsql_schema_common(Oid nspOid, uint32 required, bool abort); -+ static bool sepgsql_attribute_common(Oid relOid, AttrNumber attnum, -+ uint32 required, bool abort); -+ static bool sepgsql_relation_common(Oid relOid, uint32 required, bool abort); -+ static bool sepgsql_proc_common(Oid procOid, uint32 required, bool abort); -+ static bool sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort); -+ static bool sepgsql_foreign_server_common(Oid fsrvOid, uint32 required, bool abort); -+ static bool sepgsql_language_common(Oid langOid, uint32 required, bool abort); -+ static bool sepgsql_operator_common(Oid oprOid, uint32 required, bool abort); -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_database related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ static bool -+ sepgsql_database_common(Oid datOid, uint32 required, bool abort) -+ { -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ const char *auname; -+ bool rc; -+ -+ tuple = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(datOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for database: %u", datOid); -+ -+ auname = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); -+ -+ sid = sepgsqlGetTupleSecid(DatabaseRelationId, tuple, &tclass); -+ -+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+ } -+ -+ Oid -+ sepgsql_database_create(const char *datName, DefElem *newLabel) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!newLabel) -+ sid = sepgsqlGetDefaultDatabaseSecid(); -+ else -+ { -+ sid.relid = DatabaseRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, -+ strVal(newLabel->arg)); -+ } -+ -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_DATABASE, -+ SEPG_DB_DATABASE__CREATE, -+ datName, true); -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_database_alter(Oid datOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_database_common(datOid, SEPG_DB_DATABASE__SETATTR, true); -+ } -+ -+ void -+ sepgsql_database_drop(Oid datOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_database_common(datOid, SEPG_DB_DATABASE__DROP, true); -+ } -+ -+ Oid -+ sepgsql_database_relabel(Oid datOid, DefElem *newLabel) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux is disabled now"))); -+ -+ return InvalidOid; -+ } -+ sid.relid = DatabaseRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ -+ /* db_database:{setattr relabelfrom} to older seclabel */ -+ sepgsql_database_common(datOid, -+ SEPG_DB_DATABASE__SETATTR | -+ SEPG_DB_DATABASE__RELABELFROM, true); -+ -+ /* db_database:{relabelto} to newer seclabel */ -+ sepgsqlClientHasPerms(sid, -+ SEPG_CLASS_DB_DATABASE, -+ SEPG_DB_DATABASE__RELABELTO, -+ get_database_name(datOid), true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_database_grant(Oid datOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_database_common(datOid, SEPG_DB_DATABASE__SETATTR, true); -+ } -+ -+ void -+ sepgsql_database_access(Oid datOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_database_common(datOid, SEPG_DB_DATABASE__ACCESS, true); -+ } -+ -+ bool -+ sepgsql_database_superuser(Oid datOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return true; -+ -+ return sepgsql_database_common(datOid, SEPG_DB_DATABASE__SUPERUSER, false); -+ } -+ -+ void -+ sepgsql_database_load_module(Oid datOid, const char *filename) -+ { -+ HeapTuple tuple; -+ security_context_t filecon; -+ security_context_t datcon; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ /* -+ * It assumes preloaded libraries are secure, -+ * because it can be set up using guc variable -+ * not any SQL statements. -+ */ -+ if (GetProcessingMode() == InitProcessing) -+ return; -+ -+ /* Get database context */ -+ tuple = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(datOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for database: %u", datOid); -+ -+ datcon = securityRawSecLabelOut(DatabaseRelationId, -+ HeapTupleGetSecid(tuple)); -+ ReleaseSysCache(tuple); -+ -+ /* Get library context */ -+ if (getfilecon_raw(filename, &filecon) < 0) -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not access file \"%s\": %m", filename))); -+ PG_TRY(); -+ { -+ sepgsqlComputePerms(datcon, -+ filecon, -+ SEPG_CLASS_DB_DATABASE, -+ SEPG_DB_DATABASE__LOAD_MODULE, -+ filename, true); -+ } -+ PG_CATCH(); -+ { -+ freecon(filecon); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(filecon); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_namespace related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ static bool -+ sepgsql_schema_common(Oid nspOid, uint32 required, bool abort) -+ { -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ const char *auname; -+ bool rc; -+ -+ tuple = SearchSysCache(NAMESPACEOID, -+ ObjectIdGetDatum(nspOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for namespace: %u", nspOid); -+ -+ sid = sepgsqlGetTupleSecid(NamespaceRelationId, tuple, &tclass); -+ -+ auname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); -+ -+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+ } -+ -+ Oid -+ sepgsql_schema_create(const char *nspName, bool isTemp, DefElem *newLabel) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!newLabel) -+ sid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); -+ else -+ { -+ sid.relid = NamespaceRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ } -+ -+ sepgsqlClientHasPerms(sid, -+ SEPG_CLASS_DB_SCHEMA, -+ SEPG_DB_SCHEMA__CREATE, -+ nspName, true); -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_schema_alter(Oid nspOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SETATTR, true); -+ } -+ -+ void -+ sepgsql_schema_drop(Oid nspOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__DROP, true); -+ } -+ -+ Oid -+ sepgsql_schema_relabel(Oid nspOid, DefElem *newLabel) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ sid.relid = NamespaceRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ -+ /* db_schema:{setattr relabelfrom} for older seclabel */ -+ sepgsql_schema_common(nspOid, -+ SEPG_DB_SCHEMA__SETATTR | -+ SEPG_DB_SCHEMA__RELABELFROM, true); -+ -+ /* db_schema:{relabelto} for newer seclabel */ -+ sepgsqlClientHasPerms(sid, -+ SEPG_CLASS_DB_SCHEMA, -+ SEPG_DB_SCHEMA__RELABELTO, -+ get_namespace_name(nspOid), true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_schema_grant(Oid nspOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SETATTR, true); -+ } -+ -+ bool -+ sepgsql_schema_search(Oid nspOid, bool abort) -+ { -+ if (!sepgsqlIsEnabled()) -+ return true; -+ -+ return sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SEARCH, abort); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_attribute related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ static bool -+ sepgsql_attribute_common(Oid relOid, AttrNumber attnum, -+ uint32 required, bool abort) -+ { -+ Form_pg_attribute attForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ char auname[NAMEDATALEN * 2 + 3]; -+ bool rc = true; -+ -+ /* Caller prevent case when relkind != RELKIND_RELATION */ -+ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); -+ -+ tuple = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(relOid), -+ Int16GetDatum(attnum), -+ 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for attribute %d of relation %u", -+ attnum, relOid); -+ attForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ -+ /* -+ * NOTE: when a table to be dropped, corresponding attributes -+ * are also removed. Some of them can be already logically -+ * dropped using ALTER TABLE ... DROP statement. -+ * In this case, SE-PostgreSQL does not check anything. -+ * If any other situation touches dropped column, it is a bug. -+ */ -+ if (attForm->attisdropped) -+ goto skip; -+ -+ sprintf(auname, "%s.%s", get_rel_name(relOid), NameStr(attForm->attname)); -+ -+ sid = sepgsqlGetTupleSecid(AttributeRelationId, tuple, &tclass); -+ -+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); -+ -+ skip: -+ ReleaseSysCache(tuple); -+ -+ return rc; -+ } -+ -+ Oid -+ sepgsql_attribute_create(Oid relOid, ColumnDef *cdef) -+ { -+ sepgsql_sid_t sid; -+ char relkind; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (cdef->secLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ -+ relkind = get_rel_relkind(relOid); -+ if (relkind == RELKIND_RELATION) -+ { -+ char auname[NAMEDATALEN * 2 + 3]; -+ -+ if (!cdef->secLabel) -+ sid = sepgsqlGetDefaultColumnSecid(relOid); -+ else -+ { -+ char *label = strVal(((DefElem *)cdef->secLabel)->arg); -+ -+ sid.relid = AttributeRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, label); -+ } -+ -+ sprintf(auname, "%s.%s", get_rel_name(relOid), cdef->colname); -+ sepgsqlClientHasPerms(sid, -+ SEPG_CLASS_DB_COLUMN, -+ SEPG_DB_COLUMN__CREATE, -+ auname, true); -+ } -+ else -+ { -+ /* no need to check for toast relation */ -+ if (relkind != RELKIND_TOASTVALUE) -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ return InvalidOid; -+ } -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_attribute_alter(Oid relOid, const char *attname) -+ { -+ AttrNumber attno; -+ char relkind; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* -+ * If the target attribute does not exist, an error -+ * shall be raised later. -+ */ -+ attno = get_attnum(relOid, attname); -+ if (attno == InvalidAttrNumber) -+ return; -+ -+ relkind = get_rel_relkind(relOid); -+ if (relkind == RELKIND_RELATION) -+ { -+ sepgsql_attribute_common(relOid, attno, SEPG_DB_COLUMN__SETATTR, true); -+ } -+ else if (relkind != RELKIND_TOASTVALUE) -+ { -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ } -+ } -+ -+ void -+ sepgsql_attribute_drop(Oid relOid, AttrNumber attnum) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* -+ * We only need to check db_column:{drop} when relkind equals -+ * RELKIND_RELATION, because db_xxx:{drop} permission is already -+ * checked in other cases. (e.g DROP SEQUENCE, ...) -+ */ -+ if (get_rel_relkind(relOid) == RELKIND_RELATION) -+ sepgsql_attribute_common(relOid, attnum, -+ SEPG_DB_COLUMN__DROP, true); -+ } -+ -+ void -+ sepgsql_attribute_grant(Oid relOid, AttrNumber attnum) -+ { -+ char relkind; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ relkind = get_rel_relkind(relOid); -+ if (relkind == RELKIND_RELATION) -+ { -+ sepgsql_attribute_common(relOid, attnum, SEPG_DB_COLUMN__SETATTR, true); -+ } -+ else if (relkind != RELKIND_TOASTVALUE) -+ { -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ } -+ } -+ -+ Oid -+ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, DefElem *newLabel) -+ { -+ sepgsql_sid_t sid; -+ char auname[NAMEDATALEN * 2 + 3]; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (!newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ -+ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); -+ -+ sid.relid = AttributeRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ -+ /* db_column:{setattr relabelfrom} */ -+ sepgsql_attribute_common(relOid, attnum, -+ SEPG_DB_COLUMN__SETATTR | -+ SEPG_DB_COLUMN__RELABELFROM, true); -+ -+ /* db_column:{relabelto} */ -+ sprintf(auname, "%s.%s", -+ get_rel_name(relOid), -+ get_attname(relOid, attnum)); -+ sepgsqlClientHasPerms(sid, -+ SEPG_CLASS_DB_COLUMN, -+ SEPG_DB_COLUMN__RELABELTO, -+ auname, true); -+ -+ return sid.secid; -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_class related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ static bool -+ sepgsql_relation_common(Oid relOid, uint32 required, bool abort) -+ { -+ Form_pg_class relForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ bool rc; -+ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation %u", relOid); -+ relForm = (Form_pg_class) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(RelationRelationId, tuple, &tclass); -+ rc = sepgsqlClientHasPerms(sid, tclass, required, -+ NameStr(relForm->relname), abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+ } -+ -+ /* -+ * sepgsql_relation_create -+ * It returns an array of security identifier for the new table -+ * and columns to be assigned. The corresponding security labels -+ * are already checked for db_table/db_sequence/db_column:{create} -+ * permission. -+ * In the default labeling rule, a column inherits the security -+ * label of its table, but we cannot refer it using system caches, -+ * because the command counter is not incremented under the -+ * heap_create_with_catalog(). Thus, we need to compute and check -+ * them prior to the actual creation of table and columns. -+ */ -+ Oid * -+ sepgsql_relation_create(const char *relName, -+ char relkind, -+ TupleDesc tupDesc, -+ Oid nspOid, -+ DefElem *relLabel, -+ List *colList, -+ bool createAs, -+ bool permission) -+ { -+ Oid *secLabels; -+ sepgsql_sid_t relsid; -+ uint16 tclass; -+ uint32 required; -+ int index; -+ -+ if (!sepgsqlIsEnabled()) -+ return NULL; -+ -+ switch (relkind) -+ { -+ case RELKIND_RELATION: -+ if (!relLabel) -+ relsid = sepgsqlGetDefaultTableSecid(nspOid); -+ else -+ { -+ relsid.relid = RelationRelationId; -+ relsid.secid = securityTransSecLabelIn(relsid.relid, -+ strVal(relLabel->arg)); -+ } -+ tclass = SEPG_CLASS_DB_TABLE; -+ required = SEPG_DB_TABLE__CREATE; -+ if (createAs) -+ required |= SEPG_DB_TABLE__INSERT; -+ break; -+ -+ case RELKIND_SEQUENCE: -+ if (!relLabel) -+ relsid = sepgsqlGetDefaultSequenceSecid(nspOid); -+ else -+ { -+ relsid.relid = RelationRelationId; -+ relsid.secid = securityTransSecLabelIn(relsid.relid, -+ strVal(relLabel->arg)); -+ } -+ tclass = SEPG_CLASS_DB_SEQUENCE; -+ required = SEPG_DB_SEQUENCE__CREATE; -+ break; -+ -+ default: -+ if (!relLabel) -+ relsid = sepgsqlGetDefaultTupleSecid(RelationRelationId); -+ else -+ { -+ /* should not be happen */ -+ relsid.relid = RelationRelationId; -+ relsid.secid = securityTransSecLabelIn(relsid.relid, -+ strVal(relLabel->arg)); -+ } -+ tclass = SEPG_CLASS_DB_TUPLE; -+ required = SEPG_DB_TUPLE__INSERT; -+ break; -+ } -+ -+ /* -+ * The secLabeld array stores security identifiers to be assigned -+ * on the new table and columns. -+ * -+ * secLabels[0] is security identifier of the table. -+ * secLabels[attnum - FirstLowInvalidHeapAttributeNumber] -+ * is security identifier of columns (if necessary). -+ */ -+ secLabels = palloc0(sizeof(Oid) * (tupDesc->natts -+ - FirstLowInvalidHeapAttributeNumber)); -+ -+ /* relation's security identifier to be assigned on */ -+ secLabels[0] = relsid.secid; -+ -+ /* -+ * Note that this hook can be called during initdb processes. -+ * It is an exception of access controls, so we skip any checks. -+ * -+ * And, we don't need any checks for toast relations, because -+ * it is a quite internal stuff. -+ */ -+ if (permission) -+ { -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_table:{create}, db_sequence:{create} or db_tuple:{insert} */ -+ sepgsqlClientHasPerms(relsid, tclass, required, relName, true); -+ } -+ -+ /* no individual security context expect for RELKIND_RELATION */ -+ if (relkind != RELKIND_RELATION) -+ return secLabels; -+ -+ /* -+ * db_column:{create} permission -+ */ -+ for (index = FirstLowInvalidHeapAttributeNumber + 1; -+ index < tupDesc->natts; -+ index++) -+ { -+ Form_pg_attribute attr; -+ sepgsql_sid_t attsid = { InvalidOid, InvalidOid }; -+ char attname[NAMEDATALEN * 2 + 3]; -+ ListCell *l; -+ -+ /* skip unnecessary attributes */ -+ if (index == ObjectIdAttributeNumber && !tupDesc->tdhasoid) -+ continue; -+ -+ if (index < 0) -+ attr = SystemAttributeDefinition(index, tupDesc->tdhasoid); -+ else -+ attr = tupDesc->attrs[index]; -+ -+ /* Is there any given security context? */ -+ foreach (l, colList) -+ { -+ ColumnDef *cdef = lfirst(l); -+ -+ if (cdef->secLabel && -+ strcmp(cdef->colname, NameStr(attr->attname)) == 0) -+ { -+ attsid.relid = AttributeRelationId; -+ attsid.secid = securityTransSecLabelIn(attsid.relid, -+ strVal(((DefElem *)cdef->secLabel)->arg)); -+ break; -+ } -+ } -+ -+ /* default security context, if not given */ -+ if (!SidIsValid(attsid)) -+ attsid = sepgsqlClientCreateSecid(relsid, -+ SEPG_CLASS_DB_COLUMN, -+ AttributeRelationId); -+ if (permission) -+ { -+ required = SEPG_DB_COLUMN__CREATE; -+ -+ if (createAs) -+ required |= SEPG_DB_COLUMN__INSERT; -+ -+ /* db_column:{create (insert)} */ -+ sprintf(attname, "%s.%s", relName, NameStr(attr->attname)); -+ sepgsqlClientHasPerms(attsid, -+ SEPG_CLASS_DB_COLUMN, -+ required, attname, true); -+ } -+ /* column's security identifier to be assigend on */ -+ secLabels[index - FirstLowInvalidHeapAttributeNumber] = attsid.secid; -+ } -+ -+ return secLabels; -+ } -+ -+ /* -+ * sepgsql_relation_copy -+ * It returns an array of security identifier of table and columns -+ * to be copied on make_new_heap(). It actually create a new temporary -+ * relation and insert all the tuples within original one into the -+ * temporary one, but swap_relation_files() swaps their file nodes. -+ * Thus, there are no changes from the viewpoint of users. -+ * SE-PostgreSQL also does not check and change anything. It simply -+ * copies security identifier of the source relation to the destination -+ * relation. -+ */ -+ Oid * -+ sepgsql_relation_copy(Relation src) -+ { -+ Oid *secLabels; -+ HeapTuple tuple; -+ Oid relOid = RelationGetRelid(src); -+ int index; -+ -+ if (!sepgsqlIsEnabled()) -+ return NULL; -+ -+ /* see the comment at sepgsqlCreateTableColumn*/ -+ secLabels = palloc0(sizeof(Oid) * (RelationGetDescr(src)->natts -+ - FirstLowInvalidHeapAttributeNumber)); -+ -+ /* copy table's security identifier */ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation \"%s\"", -+ RelationGetRelationName(src)); -+ -+ secLabels[0] = HeapTupleGetSecid(tuple); -+ -+ ReleaseSysCache(tuple); -+ -+ /* copy column's security identifier */ -+ for (index = FirstLowInvalidHeapAttributeNumber + 1; -+ index < RelationGetDescr(src)->natts; -+ index++) -+ { -+ Form_pg_attribute attr; -+ -+ if (index < 0) -+ attr = SystemAttributeDefinition(index, true); -+ else -+ attr = RelationGetDescr(src)->attrs[index]; -+ -+ tuple = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(relOid), -+ Int16GetDatum(attr->attnum), -+ 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ continue; -+ -+ secLabels[index - FirstLowInvalidHeapAttributeNumber] -+ = HeapTupleGetSecid(tuple); -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ return secLabels; -+ } -+ -+ void -+ sepgsql_relation_alter(Oid relOid, const char *newName, Oid newNsp) -+ { -+ Form_pg_class relForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation %u", relOid); -+ relForm = (Form_pg_class) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(RelationRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TABLE__SETATTR, -+ NameStr(relForm->relname), true); -+ -+ /* db_schema:{add_name remove_name}, if necessary */ -+ if (newName || OidIsValid(newNsp)) -+ { -+ if (!OidIsValid(newNsp)) -+ sepgsql_schema_common(relForm->relnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ else -+ { -+ sepgsql_schema_common(relForm->relnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); -+ } -+ } -+ ReleaseSysCache(tuple); -+ } -+ -+ void -+ sepgsql_relation_drop(Oid relOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ if (get_rel_relkind(relOid) == RELKIND_TOASTVALUE) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__DROP, true); -+ } -+ -+ void -+ sepgsql_relation_grant(Oid relOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ } -+ -+ Oid -+ sepgsql_relation_relabel(Oid relOid, DefElem *newLabel) -+ { -+ sepgsql_sid_t sid; -+ char relkind; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ -+ relkind = get_rel_relkind(relOid); -+ if (relkind != RELKIND_RELATION && relkind != RELKIND_SEQUENCE) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", -+ get_rel_name(relOid)))); -+ -+ /* input security context */ -+ sid.relid = RelationRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ -+ /* db_table/db_sequence:{setattr relabelfrom} */ -+ sepgsql_relation_common(relOid, -+ SEPG_DB_TABLE__SETATTR | -+ SEPG_DB_TABLE__RELABELFROM, true); -+ -+ /* db_table/db_sequence:{relabelto} */ -+ sepgsqlClientHasPerms(sid, -+ (relkind == RELKIND_RELATION -+ ? SEPG_CLASS_DB_TABLE -+ : SEPG_CLASS_DB_SEQUENCE), -+ SEPG_DB_TABLE__RELABELTO, -+ get_rel_name(relOid), true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_relation_get_transaction_id(Oid relOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__GETATTR, true); -+ } -+ -+ void -+ sepgsql_relation_copy_definition(Oid relOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__GETATTR, true); -+ } -+ -+ void -+ sepgsql_relation_truncate(Relation rel) -+ { -+ HeapScanDesc scan; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* check db_table:{delete} permission */ -+ sepgsql_relation_common(RelationGetRelid(rel), -+ SEPG_DB_TABLE__DELETE, true); -+ -+ /* row-level access control is enabled? */ -+ if (!sepostgresql_row_level) -+ return; -+ -+ /* check db_tuple:{delete} permission */ -+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); -+ -+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) -+ { -+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NULL, true); -+ } -+ heap_endscan(scan); -+ } -+ -+ void -+ sepgsql_relation_references(Relation rel, int16 *attnums, int natts) -+ { -+ Oid relOid = RelationGetRelid(rel); -+ int i; -+ -+ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* db_table:{reference} */ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__REFERENCE, true); -+ -+ for (i=0; i < natts; i++) -+ sepgsql_attribute_common(relOid, attnums[i], -+ SEPG_DB_COLUMN__REFERENCE, true); -+ } -+ -+ void -+ sepgsql_relation_lock(Oid relOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ if (get_rel_relkind(relOid) != RELKIND_RELATION) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__LOCK, true); -+ } -+ -+ void -+ sepgsql_view_replace(Oid viewOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ Assert(get_rel_relkind(viewOid) == RELKIND_VIEW); -+ -+ sepgsql_relation_common(viewOid, SEPG_DB_TABLE__SETATTR, true); -+ } -+ -+ void -+ sepgsql_index_create(Oid relOid, Oid nspOid, bool check_rights) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ if (check_rights) -+ { -+ /* db_table:{setattr} */ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ } -+ } -+ -+ void -+ sepgsql_sequence_get_value(Oid seqOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); -+ -+ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__GET_VALUE, true); -+ } -+ -+ void -+ sepgsql_sequence_next_value(Oid seqOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); -+ -+ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__NEXT_VALUE, true); -+ } -+ -+ void -+ sepgsql_sequence_set_value(Oid seqOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); -+ -+ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__SET_VALUE, true); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_proc related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ static bool -+ sepgsql_proc_common(Oid procOid, uint32 required, bool abort) -+ { -+ sepgsql_sid_t sid; -+ HeapTuple tuple; -+ uint16 tclass; -+ const char *auname; -+ bool rc; -+ -+ tuple = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(procOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for procedure: %u", procOid); -+ -+ auname = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); -+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, tuple, &tclass); -+ -+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+ } -+ -+ Oid -+ sepgsql_proc_create(const char *procName, HeapTuple oldTup, -+ Oid nspOid, Oid langOid, DefElem *newLabel) -+ { -+ sepgsql_sid_t sid; -+ HeapTuple tuple; -+ uint32 required; -+ bool trusted; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!HeapTupleIsValid(oldTup)) -+ { -+ /* create a new function */ -+ required = SEPG_DB_PROCEDURE__CREATE; -+ if (!newLabel) -+ sid = sepgsqlGetDefaultProcedureSecid(nspOid); -+ else -+ { -+ sid.relid = ProcedureRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ } -+ } -+ else if (!newLabel) -+ { -+ /* replace an existing function, without any label */ -+ required = SEPG_DB_PROCEDURE__SETATTR; -+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, oldTup, NULL); -+ } -+ else -+ { -+ /* replace an existing function, with relabeling */ -+ sepgsql_proc_common(HeapTupleGetOid(oldTup), -+ SEPG_DB_PROCEDURE__SETATTR | -+ SEPG_DB_PROCEDURE__RELABELFROM, true); -+ -+ required = SEPG_DB_PROCEDURE__RELABELTO; -+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, oldTup, NULL); -+ } -+ -+ /* Procedural language is trusted? */ -+ tuple = SearchSysCache(LANGOID, -+ ObjectIdGetDatum(langOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for procedural langugage: %u", langOid); -+ -+ trusted = ((Form_pg_language) GETSTRUCT(tuple))->lanpltrusted; -+ if (!trusted) -+ required |= SEPG_DB_PROCEDURE__UNTRUSTED; -+ -+ ReleaseSysCache(tuple); -+ -+ /* check it */ -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_PROCEDURE, -+ required, procName, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_proc_alter(Oid procOid, const char *newName, Oid newNsp) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); -+ if (newName || OidIsValid(newNsp)) -+ { -+ HeapTuple tuple; -+ Oid oldNsp; -+ -+ tuple = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(procOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for function %u", procOid); -+ -+ oldNsp = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; -+ -+ ReleaseSysCache(tuple); -+ -+ if (!OidIsValid(newNsp)) -+ { -+ sepgsql_schema_common(oldNsp, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ else -+ { -+ sepgsql_schema_common(oldNsp, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); -+ } -+ } -+ } -+ -+ void -+ sepgsql_proc_drop(Oid procOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__DROP, true); -+ } -+ -+ void -+ sepgsql_proc_grant(Oid procOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); -+ } -+ -+ Oid -+ sepgsql_proc_relabel(Oid procOid, DefElem *newLabel) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ -+ sid.relid = ProcedureRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ -+ /* db_procedure:{setattr relabelfrom} for older seclabel */ -+ sepgsql_proc_common(procOid, -+ SEPG_DB_PROCEDURE__SETATTR | -+ SEPG_DB_PROCEDURE__RELABELFROM, true); -+ /* db_procedure:{relabelto} for newer seclabel */ -+ sepgsqlClientHasPerms(sid, -+ SEPG_CLASS_DB_PROCEDURE, -+ SEPG_DB_PROCEDURE__RELABELTO, -+ get_func_name(procOid), true); -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_proc_execute(Oid procOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__EXECUTE, true); -+ } -+ -+ bool -+ sepgsql_proc_hint_inlined(HeapTuple protup) -+ { -+ security_context_t newcon; -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return true; -+ -+ if (!sepgsql_proc_common(HeapTupleGetOid(protup), -+ SEPG_DB_PROCEDURE__EXECUTE, false)) -+ return false; -+ /* -+ * If the security context of client is unchange -+ * before or after invocation of the functions, -+ * it is not a trusted procedure, so it can be -+ * inlined due to performance purpose. -+ */ -+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, protup, NULL); -+ -+ newcon = sepgsqlClientCreateLabel(sid, SEPG_CLASS_PROCESS); -+ -+ if (strcmp(sepgsqlGetClientLabel(), newcon) == 0) -+ return true; -+ -+ return false; -+ } -+ -+ /* -+ * sepgsqlCheckProcedureEntrypoint -+ * checks whether the given function call causes domain transition, -+ * or not. If it needs a domain transition, it injects a wrapper -+ * function to invoke it under new domain. -+ */ -+ struct TrustedProcedureCache -+ { -+ FmgrInfo flinfo; -+ char newcon[1]; -+ }; -+ -+ static Datum -+ sepgsqlTrustedProcedure(PG_FUNCTION_ARGS) -+ { -+ struct TrustedProcedureCache *tcache; -+ security_context_t save_context; -+ FmgrInfo *save_flinfo; -+ Datum result; -+ -+ tcache = fcinfo->flinfo->fn_extra; -+ Assert(tcache != NULL); -+ -+ save_context = sepgsqlSwitchClient(tcache->newcon); -+ save_flinfo = fcinfo->flinfo; -+ fcinfo->flinfo = &tcache->flinfo; -+ -+ PG_TRY(); -+ { -+ result = FunctionCallInvoke(fcinfo); -+ } -+ PG_CATCH(); -+ { -+ sepgsqlSwitchClient(save_context); -+ fcinfo->flinfo = save_flinfo; -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ sepgsqlSwitchClient(save_context); -+ fcinfo->flinfo = save_flinfo; -+ -+ return result; -+ } -+ -+ void -+ sepgsql_proc_entrypoint(FmgrInfo *flinfo, HeapTuple protup) -+ { -+ struct TrustedProcedureCache *tcache; -+ security_context_t newcon; -+ sepgsql_sid_t proSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ proSid = sepgsqlGetTupleSecid(ProcedureRelationId, -+ protup, NULL); -+ -+ newcon = sepgsqlClientCreateLabel(proSid, SEPG_CLASS_PROCESS); -+ -+ /* Do nothing, if it is not a trusted procedure */ -+ if (strcmp(newcon, sepgsqlGetClientLabel()) == 0) -+ return; -+ -+ /* check db_procedure:{entrypoint} */ -+ sepgsqlClientHasPerms(proSid, -+ SEPG_CLASS_DB_PROCEDURE, -+ SEPG_DB_PROCEDURE__ENTRYPOINT, -+ NULL, true); -+ -+ /* check process:{transition} */ -+ sepgsqlComputePerms(sepgsqlGetClientLabel(), -+ newcon, -+ SEPG_CLASS_PROCESS, -+ SEPG_PROCESS__TRANSITION, -+ NULL, true); -+ -+ /* setup trusted procedure */ -+ tcache = MemoryContextAllocZero(flinfo->fn_mcxt, -+ sizeof(*tcache) + strlen(newcon)); -+ memcpy(&tcache->flinfo, flinfo, sizeof(*flinfo)); -+ strcpy(tcache->newcon, newcon); -+ flinfo->fn_addr = sepgsqlTrustedProcedure; -+ flinfo->fn_extra = tcache; -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_cast related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ Oid -+ sepgsql_cast_create(Oid sourceTypOid, Oid targetTypOid, Oid funcOid) -+ { -+ sepgsql_sid_t sid; -+ char audit_buffer[2*NAMEDATALEN+10]; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(CastRelationId); -+ -+ snprintf(audit_buffer, sizeof(audit_buffer), "%s::%s", -+ format_type_be(sourceTypOid), format_type_be(targetTypOid)); -+ -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ audit_buffer, true); -+ -+ if (OidIsValid(funcOid)) -+ sepgsql_proc_common(funcOid, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_cast_drop(Oid castOid) -+ { -+ Form_pg_cast castForm; -+ Relation rel; -+ HeapTuple tuple; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ char audit_buffer[2*NAMEDATALEN+10]; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ rel = heap_open(CastRelationId, AccessShareLock); -+ -+ ScanKeyInit(&skey, -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(castOid)); -+ -+ scan = systable_beginscan(rel, CastOidIndexId, true, -+ SnapshotNow, 1, &skey); -+ tuple = systable_getnext(scan); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "could not find tuple for cast: %u", castOid); -+ -+ castForm = (Form_pg_cast) GETSTRUCT(tuple); -+ -+ snprintf(audit_buffer, sizeof(audit_buffer), "%s::%s", -+ format_type_be(castForm->castsource), -+ format_type_be(castForm->casttarget)); -+ -+ sid = sepgsqlGetTupleSecid(CastRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ audit_buffer, true); -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, AccessShareLock); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_conversion related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ Oid -+ sepgsql_conversion_create(const char *convName, Oid nspOid, Oid procOid) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(ConversionRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ convName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{install} */ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_conversion_alter(Oid convOid, const char *newName) -+ { -+ Form_pg_conversion convForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(CONVOID, -+ ObjectIdGetDatum(convOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for conversion %u", convOid); -+ convForm = (Form_pg_conversion) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(ConversionRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(convForm->conname), true); -+ if (newName) -+ { -+ Oid nspOid = convForm->connamespace; -+ -+ sepgsql_schema_common(nspOid, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+ } -+ -+ void -+ sepgsql_conversion_drop(Oid convOid) -+ { -+ Form_pg_conversion convForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(CONVOID, -+ ObjectIdGetDatum(convOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for conversion %u", convOid); -+ convForm = (Form_pg_conversion) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(ConversionRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(convForm->conname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(convForm->connamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_foreign_data_wrapper related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ static bool -+ sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort) -+ { -+ Form_pg_foreign_data_wrapper fdwForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ bool rc; -+ -+ tuple = SearchSysCache(FOREIGNDATAWRAPPEROID, -+ ObjectIdGetDatum(fdwOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for FDW: %u", fdwOid); -+ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(ForeignDataWrapperRelationId, tuple, &tclass); -+ rc = sepgsqlClientHasPerms(sid, tclass, required, -+ NameStr(fdwForm->fdwname), abort); -+ ReleaseSysCache(tuple); -+ -+ return rc; -+ } -+ -+ Oid -+ sepgsql_fdw_create(const char *fdwName, Oid fdwValidator) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(ForeignDataWrapperRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ fdwName, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(fdwValidator)) -+ sepgsql_proc_common(fdwValidator, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_fdw_alter(Oid fdwOid, Oid newValidator) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(newValidator)) -+ sepgsql_proc_common(newValidator, SEPG_DB_PROCEDURE__INSTALL, true); -+ } -+ -+ void -+ sepgsql_fdw_drop(Oid fdwOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+ -+ void -+ sepgsql_fdw_grant(Oid fdwOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_foreign_server related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ static bool -+ sepgsql_foreign_server_common(Oid fsrvOid, uint32 required, bool abort) -+ { -+ Form_pg_foreign_server fsrvForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ bool rc; -+ -+ tuple = SearchSysCache(FOREIGNSERVEROID, -+ ObjectIdGetDatum(fsrvOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for foreign server %u", fsrvOid); -+ fsrvForm = (Form_pg_foreign_server) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(ForeignServerRelationId, tuple, &tclass); -+ rc = sepgsqlClientHasPerms(sid, tclass, required, -+ NameStr(fsrvForm->srvname), abort); -+ ReleaseSysCache(tuple); -+ -+ return rc; -+ } -+ -+ Oid -+ sepgsql_foreign_server_create(const char *fsrvName) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(ForeignServerRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ fsrvName, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_foreign_server_alter(Oid fsrvOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+ -+ void -+ sepgsql_foreign_server_drop(Oid fsrvOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+ -+ void -+ sepgsql_foreign_server_grant(Oid fsrvOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_language related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ static bool -+ sepgsql_language_common(Oid langOid, uint32 required, bool abort) -+ { -+ Form_pg_language langForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ bool rc; -+ -+ tuple = SearchSysCache(LANGOID, -+ ObjectIdGetDatum(langOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for language %u", langOid); -+ langForm = (Form_pg_language) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(LanguageRelationId, tuple, &tclass); -+ rc = sepgsqlClientHasPerms(sid, tclass, required, -+ NameStr(langForm->lanname), abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+ } -+ -+ Oid -+ sepgsql_language_create(const char *langName, Oid handlerOid, Oid validatorOid) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(LanguageRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, langName, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(handlerOid)) -+ sepgsql_proc_common(handlerOid, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(validatorOid)) -+ sepgsql_proc_common(validatorOid, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_language_alter(Oid langOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_language_common(langOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+ -+ void -+ sepgsql_language_drop(Oid langOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_language_common(langOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+ -+ void -+ sepgsql_language_grant(Oid langOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_language_common(langOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_largeobject related security hooks -+ * (need to backport v8.5 feature) -+ * ------------------------------------------------------------ */ -+ Oid -+ sepgsql_largeobject_create(Oid loid, DefElem *newLabel) -+ { -+ return InvalidOid; -+ } -+ -+ void -+ sepgsql_largeobject_alter(Oid loid, Oid newOwner) -+ {} -+ -+ void -+ sepgsql_largeobject_drop(Oid loid, bool dacSkip) -+ {} -+ -+ void -+ sepgsql_largeobject_read(Oid loid) -+ {} -+ -+ void -+ sepgsql_largeobject_write(Oid loid) -+ {} -+ -+ void -+ sepgsql_largeobject_export(Oid loid, const char *filename) -+ {} -+ -+ Oid -+ sepgsql_largeobject_import(Oid loid, const char *filename, DefElem *newLabel) -+ {} -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_opclass related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ Oid -+ sepgsql_opclass_create(const char *opcName, Oid nspOid) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(OperatorClassRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ opcName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_opclass_alter(Oid opcOid, const char *newName) -+ { -+ Form_pg_opclass opcForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(CLAOID, -+ ObjectIdGetDatum(opcOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for opclass %u", opcOid); -+ opcForm = (Form_pg_opclass) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(OperatorClassRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(opcForm->opcname), true); -+ -+ /* db_schema:{add_name remove_name} */ -+ if (newName) -+ { -+ sepgsql_schema_common(opcForm->opcnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+ } -+ -+ void -+ sepgsql_opclass_drop(Oid opcOid) -+ { -+ Form_pg_opclass opcForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(CLAOID, -+ ObjectIdGetDatum(opcOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for opclass %u", opcOid); -+ opcForm = (Form_pg_opclass) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(OperatorClassRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(opcForm->opcname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(opcForm->opcnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_opfamily related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ Oid -+ sepgsql_opfamily_create(const char *opfName, Oid nspOid) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(OperatorFamilyRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ opfName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_opfamily_alter(Oid opfOid, const char *newName) -+ { -+ Form_pg_opfamily opfForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(OPFAMILYOID, -+ ObjectIdGetDatum(opfOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for operator family: %u", opfOid); -+ opfForm = (Form_pg_opfamily) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(opfForm->opfname), true); -+ if (newName) -+ { -+ sepgsql_schema_common(opfForm->opfnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+ } -+ -+ void -+ sepgsql_opfamily_drop(Oid opfOid) -+ { -+ Form_pg_opfamily opfForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(OPFAMILYOID, -+ ObjectIdGetDatum(opfOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for operator family: %u", opfOid); -+ opfForm = (Form_pg_opfamily) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(opfForm->opfname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(opfForm->opfnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ void -+ sepgsql_opfamily_add_operator(Oid opfOid, Oid operOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* currently, do nothing here */ -+ } -+ -+ void -+ sepgsql_opfamily_add_procedure(Oid opfOid, Oid procOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* -+ * Note that db_tuple:{setattr} is already checked at the -+ * earlier phase, so db_procedure:{install} is only needed. -+ */ -+ if (OidIsValid(procOid)) -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_operator related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ static bool -+ sepgsql_operator_common(Oid oprOid, uint32 required, bool abort) -+ { -+ Form_pg_operator oprForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ bool rc; -+ -+ tuple = SearchSysCache(OPEROID, -+ ObjectIdGetDatum(oprOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for operator: %u", oprOid); -+ oprForm = (Form_pg_operator) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(OperatorRelationId, tuple, &tclass); -+ rc = sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(oprForm->oprname), abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+ } -+ -+ Oid -+ sepgsql_operator_create(const char *oprName, Oid oprOid, Oid nspOid, -+ Oid codeFn, Oid restFn, Oid joinFn) -+ { -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint32 required; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!OidIsValid(oprOid)) -+ { -+ sid = sepgsqlGetDefaultTupleSecid(OperatorRelationId); -+ required = SEPG_DB_TUPLE__INSERT; -+ } -+ else -+ { -+ tuple = SearchSysCache(OPEROID, -+ ObjectIdGetDatum(oprOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for operator %u", oprOid); -+ -+ sid = sepgsqlGetTupleSecid(OperatorRelationId, tuple, NULL); -+ -+ ReleaseSysCache(tuple); -+ -+ required = SEPG_DB_TUPLE__UPDATE; -+ } -+ -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ required, oprName, true); -+ -+ /* db_schema:{add_name} checks */ -+ if (!OidIsValid(oprOid)) -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{install} checks */ -+ if (OidIsValid(codeFn)) -+ sepgsql_proc_common(codeFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(restFn)) -+ sepgsql_proc_common(restFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(joinFn)) -+ sepgsql_proc_common(joinFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_operator_alter(Oid oprOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_operator_common(oprOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+ -+ void -+ sepgsql_operator_drop(Oid oprOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_operator_common(oprOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_rewrite related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ void -+ sepgsql_rule_create(Oid relOid, const char *ruleName) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ } -+ -+ void -+ sepgsql_rule_drop(Oid relOid, const char *ruleName) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_trigger related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ void -+ sepgsql_trigger_create(Oid relOid, const char *trigName, Oid procOid) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* db_table:{setattr} */ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ -+ /* db_procedure:{install} */ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); -+ } -+ -+ void -+ sepgsql_trigger_alter(Oid relOid, const char *trigName) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* db_table:{setattr} */ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ } -+ -+ void -+ sepgsql_trigger_drop(Oid relOid, const char *trigName) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* db_table:{setattr} */ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_type related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ Oid -+ sepgsql_ts_config_create(const char *cfgName, Oid nspOid) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(TSConfigRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ cfgName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_ts_config_alter(Oid cfgOid, const char *newName) -+ { -+ Form_pg_ts_config cfgForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSCONFIGOID, -+ ObjectIdGetDatum(cfgOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search dictionary %u", cfgOid); -+ cfgForm = (Form_pg_ts_config) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSConfigRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(cfgForm->cfgname), true); -+ if (newName) -+ { -+ sepgsql_schema_common(cfgForm->cfgnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+ } -+ -+ void -+ sepgsql_ts_config_drop(Oid cfgOid) -+ { -+ Form_pg_ts_config cfgForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSCONFIGOID, -+ ObjectIdGetDatum(cfgOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search dictionary %u", cfgOid); -+ cfgForm = (Form_pg_ts_config) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSConfigRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(cfgForm->cfgname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(cfgForm->cfgnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_type related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ Oid -+ sepgsql_ts_dict_create(const char *dictName, Oid nspOid) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(TSDictionaryRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ dictName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_ts_dict_alter(Oid dictOid, const char *newName) -+ { -+ Form_pg_ts_dict dictForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSDICTOID, -+ ObjectIdGetDatum(dictOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); -+ dictForm = (Form_pg_ts_dict) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSDictionaryRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(dictForm->dictname), true); -+ -+ /* db_schema:{add_name remove_name} */ -+ if (newName) -+ { -+ sepgsql_schema_common(dictForm->dictnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+ } -+ -+ void -+ sepgsql_ts_dict_drop(Oid dictOid) -+ { -+ Form_pg_ts_dict dictForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSDICTOID, -+ ObjectIdGetDatum(dictOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); -+ dictForm = (Form_pg_ts_dict) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSDictionaryRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(dictForm->dictname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(dictForm->dictnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_type related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ Oid -+ sepgsql_ts_parser_create(const char *prsName, Oid nspOid, -+ Oid startFn, Oid tokenFn, Oid sendFn, -+ Oid headlineFn, Oid lextypeFn) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(TSParserRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ prsName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(startFn)) -+ sepgsql_proc_common(startFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(tokenFn)) -+ sepgsql_proc_common(tokenFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(sendFn)) -+ sepgsql_proc_common(sendFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(headlineFn)) -+ sepgsql_proc_common(headlineFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(lextypeFn)) -+ sepgsql_proc_common(lextypeFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_ts_parser_alter(Oid prsOid, const char *newName) -+ { -+ Form_pg_ts_parser prsForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSPARSEROID, -+ ObjectIdGetDatum(prsOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search parser %u", prsOid); -+ -+ prsForm = (Form_pg_ts_parser) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSParserRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(prsForm->prsname), true); -+ if (newName) -+ { -+ sepgsql_schema_common(prsForm->prsnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+ } -+ -+ void -+ sepgsql_ts_parser_drop(Oid prsOid) -+ { -+ Form_pg_ts_parser prsForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSPARSEROID, -+ ObjectIdGetDatum(prsOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search parser %u", prsOid); -+ -+ prsForm = (Form_pg_ts_parser) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSParserRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(prsForm->prsname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(prsForm->prsnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_type related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ Oid -+ sepgsql_ts_template_create(const char *tmplName, Oid nspOid, -+ Oid initFn, Oid lexizeFn) -+ { -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(TSTemplateRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ tmplName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(initFn)) -+ sepgsql_proc_common(initFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(lexizeFn)) -+ sepgsql_proc_common(lexizeFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_ts_template_alter(Oid tmplOid, const char *newName) -+ { -+ Form_pg_ts_template tmplForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSTEMPLATEOID, -+ ObjectIdGetDatum(tmplOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search template %u", tmplOid); -+ tmplForm = (Form_pg_ts_template) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSTemplateRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(tmplForm->tmplname), true); -+ if (newName) -+ { -+ sepgsql_schema_common(tmplForm->tmplnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+ } -+ -+ void -+ sepgsql_ts_template_drop(Oid tmplOid) -+ { -+ Form_pg_ts_template tmplForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSTEMPLATEOID, -+ ObjectIdGetDatum(tmplOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search template %u", tmplOid); -+ tmplForm = (Form_pg_ts_template) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSTemplateRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(tmplForm->tmplname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(tmplForm->tmplnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Pg_type related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ Oid -+ sepgsql_type_create(const char *typName, HeapTuple oldTup, Oid nspOid, -+ Oid inputProc, Oid outputProc, Oid recvProc, Oid sendProc, -+ Oid modinProc, Oid modoutProc, Oid analyzeProc) -+ { -+ sepgsql_sid_t sid; -+ uint32 required; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!HeapTupleIsValid(oldTup)) -+ { -+ sid = sepgsqlGetDefaultTupleSecid(TypeRelationId); -+ required = SEPG_DB_TUPLE__INSERT; -+ } -+ else -+ { -+ sid = sepgsqlGetTupleSecid(TypeRelationId, oldTup, NULL); -+ required = SEPG_DB_TUPLE__UPDATE; -+ } -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ required, typName, true); -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(inputProc)) -+ sepgsql_proc_common(inputProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(outputProc)) -+ sepgsql_proc_common(outputProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(recvProc)) -+ sepgsql_proc_common(recvProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(sendProc)) -+ sepgsql_proc_common(sendProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(modinProc)) -+ sepgsql_proc_common(modinProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(modoutProc)) -+ sepgsql_proc_common(modoutProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(analyzeProc)) -+ sepgsql_proc_common(analyzeProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+ } -+ -+ void -+ sepgsql_type_alter(Oid typOid, const char *newName, Oid newNsp) -+ { -+ Form_pg_type typForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TYPEOID, -+ ObjectIdGetDatum(typOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for type: %u", typOid); -+ typForm = (Form_pg_type) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TypeRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(typForm->typname), true); -+ -+ if (newName || OidIsValid(newNsp)) -+ { -+ Oid oldNsp = typForm->typnamespace; -+ -+ if (!OidIsValid(newNsp)) -+ { -+ sepgsql_schema_common(oldNsp, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ else -+ { -+ sepgsql_schema_common(oldNsp, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); -+ } -+ } -+ ReleaseSysCache(tuple); -+ } -+ -+ void -+ sepgsql_type_drop(Oid typOid) -+ { -+ Form_pg_type typForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TYPEOID, -+ ObjectIdGetDatum(typOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for type: %u", typOid); -+ typForm = (Form_pg_type) GETSTRUCT(tuple); -+ -+ if (typForm->typtype == TYPTYPE_COMPOSITE || -+ (typForm->typtype == TYPTYPE_BASE && OidIsValid(typForm->typarray))) -+ { -+ /* -+ * No need to check for composite type and implicitly -+ * declared array type here. -+ */ -+ ReleaseSysCache(tuple); -+ return; -+ } -+ -+ sid = sepgsqlGetTupleSecid(TypeRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(typForm->typname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(typForm->typnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Misc system object related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ -+ void -+ sepgsql_sysobj_drop(const ObjectAddress *object) -+ { -+ switch (object->classId) -+ { -+ case RelationRelationId: -+ if (object->objectSubId == 0) -+ sepgsql_relation_drop(object->objectId); -+ else -+ sepgsql_attribute_drop(object->objectId, -+ object->objectSubId); -+ break; -+ -+ case ProcedureRelationId: -+ sepgsql_proc_drop(object->objectId); -+ break; -+ -+ case TypeRelationId: -+ sepgsql_type_drop(object->objectId); -+ break; -+ -+ case CastRelationId: -+ sepgsql_cast_drop(object->objectId); -+ break; -+ -+ case ConversionRelationId: -+ sepgsql_conversion_drop(object->objectId); -+ break; -+ -+ case LanguageRelationId: -+ sepgsql_language_drop(object->objectId); -+ break; -+ -+ case OperatorRelationId: -+ sepgsql_operator_drop(object->objectId); -+ break; -+ -+ case OperatorClassRelationId: -+ sepgsql_opclass_drop(object->objectId); -+ break; -+ -+ case OperatorFamilyRelationId: -+ sepgsql_opfamily_drop(object->objectId); -+ break; -+ -+ case NamespaceRelationId: -+ sepgsql_schema_drop(object->objectId); -+ break; -+ -+ case TSParserRelationId: -+ sepgsql_ts_parser_drop(object->objectId); -+ break; -+ -+ case TSDictionaryRelationId: -+ sepgsql_ts_dict_drop(object->objectId); -+ break; -+ -+ case TSTemplateRelationId: -+ sepgsql_ts_template_drop(object->objectId); -+ break; -+ -+ case TSConfigRelationId: -+ sepgsql_ts_config_drop(object->objectId); -+ break; -+ -+ case AuthIdRelationId: -+ break; -+ -+ case DatabaseRelationId: -+ sepgsql_database_drop(object->objectId); -+ break; -+ -+ case TableSpaceRelationId: -+ break; -+ -+ case ForeignDataWrapperRelationId: -+ sepgsql_fdw_drop(object->objectId); -+ break; -+ -+ case ForeignServerRelationId: -+ sepgsql_foreign_server_drop(object->objectId); -+ break; -+ -+ case UserMappingRelationId: -+ break; -+ -+ default: -+ /* do nothing */ -+ break; -+ } -+ } -+ -+ /* ------------------------------------------------------------ * -+ * -+ * Filesystem object related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ static char * -+ sepgsql_getfilecon(const char *path) -+ { -+ security_context_t context; -+ char *result; -+ -+ if (getfilecon_raw(path, &context) < 0) -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not get context of \"%s\": %m", path))); -+ -+ PG_TRY(); -+ { -+ result = pstrdup(context); -+ } -+ PG_CATCH(); -+ { -+ freecon(context); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(context); -+ -+ return result; -+ } -+ -+ static void -+ sepgsql_file_common(const char *filename, uint32 required, bool may_create) -+ { -+ struct stat stbuf; -+ -+ if (stat(filename, &stbuf) == 0) -+ { -+ uint16 tclass; -+ -+ /* -+ * Get file object class -+ */ -+ if (S_ISDIR(stbuf.st_mode)) -+ tclass = SEPG_CLASS_DIR; -+ else if (S_ISCHR(stbuf.st_mode)) -+ tclass = SEPG_CLASS_CHR_FILE; -+ else if (S_ISBLK(stbuf.st_mode)) -+ tclass = SEPG_CLASS_BLK_FILE; -+ else if (S_ISFIFO(stbuf.st_mode)) -+ tclass = SEPG_CLASS_FIFO_FILE; -+ else if (S_ISLNK(stbuf.st_mode)) -+ tclass = SEPG_CLASS_LNK_FILE; -+ else if (S_ISSOCK(stbuf.st_mode)) -+ tclass = SEPG_CLASS_SOCK_FILE; -+ else -+ tclass = SEPG_CLASS_FILE; -+ -+ /* -+ * Check permission (no cached operation) -+ */ -+ sepgsqlComputePerms(sepgsqlGetClientLabel(), -+ sepgsql_getfilecon(filename), -+ tclass, required, -+ filename, true); -+ } -+ else if (may_create) -+ { -+ /* -+ * If the required file is not found, we check permission to -+ * create a new file and required permission on the new file. -+ */ -+ security_context_t dcontext; -+ security_context_t ncontext; -+ char *copy = pstrdup(filename); -+ -+ /* -+ * Compute a security context for the new file -+ */ -+ dcontext = sepgsql_getfilecon(dirname(copy)); -+ -+ ncontext = sepgsqlComputeCreate(sepgsqlGetServerLabel(), -+ dcontext, -+ SEPG_CLASS_FILE); -+ /* -+ * Check permission (no cached operation) -+ */ -+ required |= SEPG_FILE__CREATE; -+ -+ sepgsqlComputePerms(sepgsqlGetClientLabel(), -+ sepgsql_getfilecon(filename), -+ SEPG_CLASS_FILE, -+ required, filename, true); -+ } -+ else -+ { -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not stat file \"%s\": %m", filename))); -+ } -+ } -+ -+ void -+ sepgsql_file_stat(const char *filename) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_file_common(filename, SEPG_FILE__GETATTR, false); -+ } -+ -+ void -+ sepgsql_file_read(const char *filename) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_file_common(filename, SEPG_FILE__READ, false); -+ } -+ -+ void -+ sepgsql_file_write(const char *filename) -+ { -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_file_common(filename, SEPG_FILE__WRITE, true); -+ } -+ -+ /* -+ * TODO: add check for pg_ls_dir() -+ */ -diff -Nrpc base/src/backend/security/sepgsql/checker.c sepgsql/src/backend/security/sepgsql/checker.c -*** base/src/backend/security/sepgsql/checker.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/checker.c Thu Oct 8 09:29:32 2009 -*************** -*** 0 **** ---- 1,424 ---- -+ /* -+ * src/backend/security/sepgsql/checker.c -+ * walks on given Query tree and applies checks -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+ #include "postgres.h" -+ -+ #include "access/sysattr.h" -+ #include "catalog/catalog.h" -+ #include "catalog/pg_security.h" -+ #include "miscadmin.h" -+ #include "security/sepgsql.h" -+ #include "storage/bufmgr.h" -+ #include "utils/lsyscache.h" -+ #include "utils/syscache.h" -+ #include "utils/tqual.h" -+ -+ /* -+ * fixupWholeRowReference -+ */ -+ static Bitmapset * -+ fixupWholeRowReference(Oid relid, int nattrs, Bitmapset *columns) -+ { -+ Bitmapset *result; -+ AttrNumber attno; -+ -+ attno = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber; -+ -+ if (!bms_is_member(attno, columns)) -+ return columns; /* no need to fixup */ -+ -+ result = bms_copy(columns); -+ result = bms_del_member(result, attno); -+ -+ for (attno=1; attno <= nattrs; attno++) -+ { -+ Form_pg_attribute attform; -+ HeapTuple atttup; -+ -+ atttup = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(relid), -+ Int16GetDatum(attno), -+ 0, 0); -+ if (!HeapTupleIsValid(atttup)) -+ continue; -+ -+ attform = (Form_pg_attribute) GETSTRUCT(atttup); -+ if (!attform->attisdropped) -+ { -+ int cindex = attno - FirstLowInvalidHeapAttributeNumber; -+ result = bms_add_member(result, cindex); -+ } -+ ReleaseSysCache(atttup); -+ } -+ -+ return result; -+ } -+ -+ /* -+ * checkTabelColumnPerms -+ * This functions applies table/column level permissions for -+ * all the appeared ones in user's query, and raises an error -+ * if violated. -+ * It also applies a few hardwired policy which prevent to -+ * modified some of system catalogs. -+ */ -+ static void -+ checkTabelColumnPerms(Oid relid, Bitmapset *selected, Bitmapset *modified, -+ access_vector_t required) -+ { -+ Bitmapset *columns; -+ Bitmapset *selected_ex; -+ Bitmapset *modified_ex; -+ Form_pg_class relForm; -+ HeapTuple reltup; -+ sepgsql_sid_t relsid; -+ sepgsql_sid_t attsid; -+ AttrNumber attno; -+ uint16 tclass; -+ -+ /* -+ * Hardwired Policy: -+ * SE-PostgreSQL enforces that clients cannot modify system -+ * catalogs and access toast values using DML statements, -+ * except initial setting up phase. -+ */ -+ if (sepgsqlGetEnforce()) -+ { -+ if (IsSystemNamespace(get_rel_namespace(relid)) && -+ (required & (SEPG_DB_TABLE__UPDATE | -+ SEPG_DB_TABLE__INSERT | -+ SEPG_DB_TABLE__DELETE)) != 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SE-PostgreSQL prevents to modidy \"%s\"", -+ get_rel_name(relid)))); -+ if (get_rel_relkind(relid) == RELKIND_TOASTVALUE) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SE-PostgreSQL prevents to access \"%s\"", -+ get_rel_name(relid)))); -+ } -+ -+ /* -+ * Check db_table:{...} or db_sequence permissions -+ */ -+ reltup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(reltup)) -+ elog(ERROR, "SELinux: cache lookup failed for relation %u", relid); -+ -+ relForm = (Form_pg_class) GETSTRUCT(reltup); -+ -+ relsid = sepgsqlGetTupleSecid(RelationRelationId, reltup, &tclass); -+ -+ if (tclass != SEPG_CLASS_DB_TABLE) -+ { -+ /* check db_sequence:{xxx} permission */ -+ if (tclass == SEPG_CLASS_DB_SEQUENCE) -+ { -+ if (required & SEPG_DB_TABLE__SELECT) -+ { -+ sepgsqlClientHasPerms(relsid, tclass, -+ SEPG_DB_SEQUENCE__GET_VALUE, -+ NameStr(relForm->relname), true); -+ } -+ } -+ ReleaseSysCache(reltup); -+ return; -+ } -+ sepgsqlClientHasPerms(relsid, tclass, required, -+ NameStr(relForm->relname), true); -+ -+ /* -+ * Check db_column:{...} permissions -+ */ -+ selected_ex = fixupWholeRowReference(relid, relForm->relnatts, selected); -+ modified_ex = fixupWholeRowReference(relid, relForm->relnatts, modified); -+ columns = bms_union(selected_ex, modified_ex); -+ -+ while ((attno = bms_first_member(columns)) >= 0) -+ { -+ Form_pg_attribute attForm; -+ HeapTuple atttup; -+ uint32 attperms = 0; -+ char auname[2 * NAMEDATALEN + 3]; -+ -+ if (bms_is_member(attno, selected_ex)) -+ attperms |= SEPG_DB_COLUMN__SELECT; -+ if (bms_is_member(attno, modified_ex)) -+ { -+ if (required & SEPG_DB_TABLE__UPDATE) -+ attperms |= SEPG_DB_COLUMN__UPDATE; -+ if (required & SEPG_DB_TABLE__INSERT) -+ attperms |= SEPG_DB_COLUMN__INSERT; -+ } -+ if (attperms == 0) -+ continue; -+ -+ /* remove the attribute number offset */ -+ attno += FirstLowInvalidHeapAttributeNumber; -+ atttup = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(relid), -+ Int16GetDatum(attno), -+ 0, 0); -+ if (!HeapTupleIsValid(atttup)) -+ elog(ERROR, "cache lookup failed for attribute %d of relation %u", -+ attno, relid); -+ -+ attForm = (Form_pg_attribute) GETSTRUCT(atttup); -+ if (attForm->attisdropped) -+ elog(ERROR, "attribute %d of relation %u does not exist", -+ attno, relid); -+ -+ snprintf(auname, sizeof(auname), "%s.%s", -+ NameStr(relForm->relname), -+ NameStr(attForm->attname)); -+ attsid = sepgsqlGetTupleSecid(AttributeRelationId, -+ atttup, &tclass); -+ sepgsqlClientHasPerms(attsid, tclass, attperms, auname, true); -+ -+ ReleaseSysCache(atttup); -+ } -+ -+ ReleaseSysCache(reltup); -+ -+ if (selected_ex != selected) -+ bms_free(selected_ex); -+ -+ if (modified_ex != modified) -+ bms_free(modified_ex); -+ -+ bms_free(columns); -+ } -+ -+ /* -+ * sepgsqlCheckQueryPerms -+ * It checks permission for all the required tables/columns on -+ * generic user queries. -+ */ -+ void -+ sepgsqlCheckRTEPerms(RangeTblEntry *rte) -+ { -+ access_vector_t required = 0; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ if (rte->rtekind != RTE_RELATION) -+ return; -+ -+ if (rte->requiredPerms & ACL_SELECT) -+ required |= SEPG_DB_TABLE__SELECT; -+ if (rte->requiredPerms & ACL_INSERT) -+ required |= SEPG_DB_TABLE__INSERT; -+ if (rte->requiredPerms & ACL_UPDATE) -+ { -+ /* -+ * ACL_SELECT_FOR_UPDATE is defined as an aliase of ACL_UPDATE, -+ * so we cannot determine whether the given relation is accessed -+ * with UPDATE statement or SELECT FOR SHARE/UPDATE immediately. -+ * UPDATE statements set a bit on rte->modifiedCols at least, -+ * so we use it as a watermark. -+ */ -+ if (!bms_is_empty(rte->modifiedCols)) -+ required |= SEPG_DB_TABLE__UPDATE; -+ else -+ required |= SEPG_DB_TABLE__LOCK; -+ } -+ if (rte->requiredPerms & ACL_DELETE) -+ required |= SEPG_DB_TABLE__DELETE; -+ -+ if (required == 0) -+ return; -+ -+ checkTabelColumnPerms(rte->relid, -+ rte->selectedCols, -+ rte->modifiedCols, -+ required); -+ } -+ -+ /* -+ * sepgsqlCheckCopyTable -+ * It checks permissions on COPY TO/FROM. -+ */ -+ void -+ sepgsqlCheckCopyTable(Relation rel, List *attnumlist, bool is_from) -+ { -+ Bitmapset *selected = NULL; -+ Bitmapset *modified = NULL; -+ ListCell *l; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* all checkes are done in sepgsqlCheckRTEPerms */ -+ if (!rel) -+ return; -+ -+ foreach (l, attnumlist) -+ { -+ AttrNumber attno = lfirst_int(l); -+ -+ attno -= FirstLowInvalidHeapAttributeNumber; -+ if (is_from) -+ modified = bms_add_member(modified, attno); -+ else -+ selected = bms_add_member(selected, attno); -+ } -+ -+ checkTabelColumnPerms(RelationGetRelid(rel), -+ selected, modified, -+ is_from ? SEPG_DB_TABLE__INSERT -+ : SEPG_DB_TABLE__SELECT); -+ } -+ -+ /* -+ * sepgsqlExecScan -+ * makes a decision on the given tuple. -+ */ -+ bool -+ sepgsqlExecScan(Relation rel, HeapTuple tuple, uint32 required, bool abort) -+ { -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled() || -+ !required || -+ RelationGetForm(rel)->relkind != RELKIND_RELATION || -+ RelationGetRelid(rel) == SecurityRelationId) -+ return true; -+ -+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass); -+ /* -+ * Insert/Delete to an external attribute is equivalent to -+ * the set-attribute on the master -+ */ -+ if (sid.relid != RelationGetRelid(rel) && -+ (required & (SEPG_DB_TUPLE__INSERT | SEPG_DB_TUPLE__DELETE))) -+ { -+ required &= ~(SEPG_DB_TUPLE__INSERT | SEPG_DB_TUPLE__DELETE); -+ required |= SEPG_DB_TUPLE__UPDATE; -+ } -+ -+ return sepgsqlClientHasPerms(sid, tclass, required, NULL, abort); -+ } -+ -+ uint32 -+ sepgsqlSetupTuplePerms(RangeTblEntry *rte) -+ { -+ AclMode perms = 0; -+ -+ if (!sepgsqlIsEnabled()) -+ return 0; -+ -+ if (rte->rtekind != RTE_RELATION) -+ return 0; -+ -+ if (rte->requiredPerms & ACL_SELECT) -+ perms |= SEPG_DB_TUPLE__SELECT; -+ if (rte->requiredPerms & ACL_UPDATE && !bms_is_empty(rte->modifiedCols)) -+ perms |= SEPG_DB_TUPLE__UPDATE; -+ if (rte->requiredPerms & ACL_DELETE) -+ perms |= SEPG_DB_TUPLE__DELETE; -+ -+ return perms; -+ } -+ -+ /* -+ * sepgsqlHeapTupleInsert -+ * It assigns a default security label, if no explicit security labels -+ * were given. In addition, it also checks db_tuple:{insert} for the -+ * tuple newly inserted, when it invoked from user's query. -+ */ -+ void -+ sepgsqlHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal) -+ { -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* -+ * assigns a default security label, if not explicit one -+ */ -+ if (!OidIsValid(HeapTupleGetSecid(newtup))) -+ { -+ if (HeapTupleHasSecid(newtup)) -+ sepgsqlSetDefaultSecid(rel, newtup); -+ } -+ -+ /* -+ * It does not check permission for the new tuples -+ * inserted by system internal stuff using -+ * simple_heap_insert(); -+ */ -+ if (internal) -+ return; -+ -+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), -+ newtup, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, SEPG_DB_TUPLE__INSERT, NULL, true); -+ } -+ -+ /* -+ * sepgsqlHeapTupleUpdate -+ * It checks db_tuple:{relabelfrom relabelto} permission on -+ * the user queries. (Please note that it does not check -+ * system internal stuff via simple_heap_update) -+ */ -+ void -+ sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup) -+ { -+ Oid secid; -+ HeapTupleData oldtup; -+ Buffer oldbuf; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* -+ * heap_update() preserves the original security label -+ * of the given tuple, if no explicit security label -+ * is assigned on the newer version. -+ * In this case, db_tuple:{update} is already checked -+ * at the sepgsqlExecScan() hook, so we don't need to -+ * check anything more. -+ */ -+ secid = HeapTupleGetSecid(newtup); -+ if (!OidIsValid(secid)) -+ return; -+ -+ /* -+ * User gave an explicit security label -+ */ -+ ItemPointerCopy(otid, &oldtup.t_self); -+ if (!heap_fetch(rel, SnapshotAny, &oldtup, &oldbuf, false, NULL)) -+ elog(ERROR, "failed to fetch old version of the tuple"); -+ -+ if (secid != HeapTupleGetSecid(&oldtup)) -+ { -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ /* db_tuple:{relabelfrom} for older security context */ -+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), -+ &oldtup, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__RELABELFROM, -+ NULL, true); -+ -+ /* db_tuple:{relabelto} for newer security label */ -+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), -+ newtup, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__RELABELTO, -+ NULL, true); -+ } -+ ReleaseBuffer(oldbuf); -+ } -diff -Nrpc base/src/backend/security/sepgsql/dummy.c sepgsql/src/backend/security/sepgsql/dummy.c -*** base/src/backend/security/sepgsql/dummy.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/dummy.c Wed Jul 15 19:39:56 2009 -*************** -*** 0 **** ---- 1,79 ---- -+ /* -+ * src/backend/utils/sepgsql/dummy.c -+ * A set of stubs when SE-PostgreSQL is not activated -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+ #include "postgres.h" -+ -+ #include "security/sepgsql.h" -+ -+ static Datum -+ unavailable_function(const char *fn_name) -+ { -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("function \"%s\" is not available", fn_name))); -+ PG_RETURN_VOID(); -+ } -+ -+ Datum -+ sepgsql_getcon(PG_FUNCTION_ARGS) -+ { -+ return unavailable_function(__FUNCTION__); -+ } -+ -+ Datum -+ sepgsql_server_getcon(PG_FUNCTION_ARGS) -+ { -+ return unavailable_function(__FUNCTION__); -+ } -+ -+ Datum -+ sepgsql_get_user(PG_FUNCTION_ARGS) -+ { -+ return unavailable_function(__FUNCTION__); -+ } -+ -+ Datum -+ sepgsql_get_role(PG_FUNCTION_ARGS) -+ { -+ return unavailable_function(__FUNCTION__); -+ } -+ -+ Datum -+ sepgsql_get_type(PG_FUNCTION_ARGS) -+ { -+ return unavailable_function(__FUNCTION__); -+ } -+ -+ Datum -+ sepgsql_get_range(PG_FUNCTION_ARGS) -+ { -+ return unavailable_function(__FUNCTION__); -+ } -+ -+ Datum -+ sepgsql_set_user(PG_FUNCTION_ARGS) -+ { -+ return unavailable_function(__FUNCTION__); -+ } -+ -+ Datum -+ sepgsql_set_role(PG_FUNCTION_ARGS) -+ { -+ return unavailable_function(__FUNCTION__); -+ } -+ -+ Datum -+ sepgsql_set_type(PG_FUNCTION_ARGS) -+ { -+ return unavailable_function(__FUNCTION__); -+ } -+ -+ Datum -+ sepgsql_set_range(PG_FUNCTION_ARGS) -+ { -+ return unavailable_function(__FUNCTION__); -+ } -diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/security/sepgsql/hooks.c -*** base/src/backend/security/sepgsql/hooks.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/hooks.c Mon Sep 28 10:07:19 2009 -*************** -*** 0 **** ---- 1,239 ---- -+ /* -+ * src/backend/security/sepgsql/hooks.c -+ * SE-PostgreSQL security hooks -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+ #include "postgres.h" -+ -+ #include "catalog/indexing.h" -+ #include "catalog/namespace.h" -+ #include "catalog/pg_database.h" -+ #include "catalog/pg_foreign_data_wrapper.h" -+ #include "catalog/pg_language.h" -+ #include "catalog/pg_largeobject.h" -+ #include "catalog/pg_namespace.h" -+ #include "catalog/pg_opclass.h" -+ #include "catalog/pg_operator.h" -+ #include "catalog/pg_opfamily.h" -+ #include "catalog/pg_proc.h" -+ #include "catalog/pg_security.h" -+ #include "catalog/pg_trigger.h" -+ #include "catalog/pg_ts_dict.h" -+ #include "catalog/pg_ts_parser.h" -+ #include "catalog/pg_ts_template.h" -+ #include "catalog/pg_type.h" -+ #include "catalog/pg_security.h" -+ #include "commands/dbcommands.h" -+ #include "miscadmin.h" -+ #include "security/sepgsql.h" -+ #include "utils/builtins.h" -+ #include "utils/fmgroids.h" -+ #include "utils/lsyscache.h" -+ #include "utils/syscache.h" -+ #include "utils/tqual.h" -+ -+ /* ------------------------------------------------------------ * -+ * Hooks corresponding to db_blob object class -+ * ------------------------------------------------------------ */ -+ -+ /* -+ * sepgsqlCheckBlobCreate -+ * assigns a default security label and checks db_blob:{create} -+ */ -+ void -+ sepgsqlCheckBlobCreate(Relation rel, HeapTuple lotup) -+ { -+ sepgsql_sid_t loSid; -+ Oid relid = RelationGetRelid(rel); -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* set a default security context */ -+ sepgsqlSetDefaultSecid(rel, lotup); -+ -+ loSid = sepgsqlGetTupleSecid(relid, lotup, NULL); -+ sepgsqlClientHasPerms(loSid, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__CREATE, -+ NULL, true); -+ } -+ -+ /* -+ * sepgsqlCheckBlobDrop -+ * checks db_blob:{drop} permission -+ */ -+ void -+ sepgsqlCheckBlobDrop(Relation rel, HeapTuple lotup) -+ { -+ sepgsql_sid_t loSid; -+ Oid relid = RelationGetRelid(rel); -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ loSid = sepgsqlGetTupleSecid(relid, lotup, NULL); -+ sepgsqlClientHasPerms(loSid, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__DROP, -+ NULL, true); -+ } -+ -+ /* -+ * sepgsqlCheckBlobRead -+ * checks db_blob:{read} permission -+ */ -+ void -+ sepgsqlCheckBlobRead(LargeObjectDesc *lobj) -+ { -+ sepgsql_sid_t loSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ loSid.relid = LargeObjectRelationId; -+ loSid.secid = lobj->secid; -+ sepgsqlClientHasPerms(loSid, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__READ, -+ NULL, true); -+ } -+ -+ /* -+ * sepgsqlCheckBlobWrite -+ * check db_blob:{write} permission -+ */ -+ void -+ sepgsqlCheckBlobWrite(LargeObjectDesc *lobj) -+ { -+ sepgsql_sid_t loSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ loSid.relid = LargeObjectRelationId; -+ loSid.secid = lobj->secid; -+ sepgsqlClientHasPerms(loSid, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__WRITE, -+ NULL, true); -+ } -+ -+ /* -+ * sepgsqlCheckBlobGetattr -+ * check db_blob:{getattr} permission -+ */ -+ void -+ sepgsqlCheckBlobGetattr(HeapTuple tuple) -+ { -+ sepgsql_sid_t loSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ loSid.relid = LargeObjectRelationId; -+ loSid.secid = HeapTupleGetSecid(tuple); -+ sepgsqlClientHasPerms(loSid, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__GETATTR, -+ NULL, true); -+ } -+ -+ /* -+ * sepgsqlCheckBlobSetattr -+ * check db_blob:{setattr} permission -+ */ -+ void -+ sepgsqlCheckBlobSetattr(HeapTuple tuple) -+ { -+ sepgsql_sid_t loSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ loSid.relid = LargeObjectRelationId; -+ loSid.secid = HeapTupleGetSecid(tuple); -+ sepgsqlClientHasPerms(loSid, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__SETATTR, -+ NULL, true); -+ } -+ -+ /* -+ * sepgsqlCheckBlobExport -+ * check db_blob:{read export} and file:{write} permission -+ */ -+ void -+ sepgsqlCheckBlobExport(LargeObjectDesc *lobj, const char *filename) -+ { -+ sepgsql_sid_t loSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* db_blob:{read export} */ -+ loSid.relid = LargeObjectRelationId; -+ loSid.secid = lobj->secid; -+ sepgsqlClientHasPerms(loSid, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__READ | SEPG_DB_BLOB__EXPORT, -+ NULL, true); -+ /* file:{write} */ -+ sepgsql_file_write(filename); -+ } -+ -+ /* -+ * sepgsqlCheckBlobImport -+ * check db_blob:{write import} and file:{read} permission -+ */ -+ void -+ sepgsqlCheckBlobImport(LargeObjectDesc *lobj, const char *filename) -+ { -+ sepgsql_sid_t loSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* db_blob:{write import} */ -+ loSid.relid = LargeObjectRelationId; -+ loSid.secid = lobj->secid; -+ sepgsqlClientHasPerms(loSid, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__WRITE | SEPG_DB_BLOB__IMPORT, -+ NULL, true); -+ /* file:{read} */ -+ sepgsql_file_read(filename); -+ } -+ -+ /* -+ * sepgsqlCheckBlobRelabel -+ * check db_blob:{setattr relabelfrom relabelto} -+ */ -+ void -+ sepgsqlCheckBlobRelabel(HeapTuple oldtup, HeapTuple newtup) -+ { -+ sepgsql_sid_t loSid; -+ access_vector_t required = SEPG_DB_BLOB__SETATTR; -+ -+ if (HeapTupleGetSecid(oldtup) != HeapTupleGetSecid(newtup)) -+ required |= SEPG_DB_BLOB__RELABELFROM; -+ -+ /* db_blob:{setattr relabelfrom} */ -+ loSid = sepgsqlGetTupleSecid(LargeObjectRelationId, oldtup, NULL); -+ sepgsqlClientHasPerms(loSid, -+ SEPG_CLASS_DB_BLOB, -+ required, -+ NULL, true); -+ -+ if ((required & SEPG_DB_BLOB__RELABELFROM) == 0) -+ return; -+ -+ /* db_blob:{relabelto} */ -+ loSid = sepgsqlGetTupleSecid(LargeObjectRelationId, newtup, NULL); -+ sepgsqlClientHasPerms(loSid, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__RELABELTO, -+ NULL, true); -+ } -diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/security/sepgsql/label.c -*** base/src/backend/security/sepgsql/label.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/label.c Fri Sep 25 17:27:05 2009 -*************** -*** 0 **** ---- 1,1140 ---- -+ /* -+ * src/backend/security/sepgsql/label.c -+ * SE-PostgreSQL security label management -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+ #include "postgres.h" -+ -+ #include "access/sysattr.h" -+ #include "access/xact.h" -+ #include "catalog/catalog.h" -+ #include "catalog/pg_constraint.h" -+ #include "catalog/heap.h" -+ #include "catalog/indexing.h" -+ #include "catalog/namespace.h" -+ #include "catalog/pg_aggregate.h" -+ #include "catalog/pg_amop.h" -+ #include "catalog/pg_amproc.h" -+ #include "catalog/pg_attrdef.h" -+ #include "catalog/pg_attribute.h" -+ #include "catalog/pg_auth_members.h" -+ #include "catalog/pg_authid.h" -+ #include "catalog/pg_cast.h" -+ #include "catalog/pg_class.h" -+ #include "catalog/pg_conversion.h" -+ #include "catalog/pg_database.h" -+ #include "catalog/pg_description.h" -+ #include "catalog/pg_enum.h" -+ #include "catalog/pg_foreign_data_wrapper.h" -+ #include "catalog/pg_foreign_server.h" -+ #include "catalog/pg_inherits.h" -+ #include "catalog/pg_language.h" -+ #include "catalog/pg_largeobject.h" -+ #include "catalog/pg_namespace.h" -+ #include "catalog/pg_opclass.h" -+ #include "catalog/pg_operator.h" -+ #include "catalog/pg_opfamily.h" -+ #include "catalog/pg_proc.h" -+ #include "catalog/pg_rewrite.h" -+ #include "catalog/pg_security.h" -+ #include "catalog/pg_shdescription.h" -+ #include "catalog/pg_statistic.h" -+ #include "catalog/pg_tablespace.h" -+ #include "catalog/pg_trigger.h" -+ #include "catalog/pg_ts_config.h" -+ #include "catalog/pg_ts_config_map.h" -+ #include "catalog/pg_ts_dict.h" -+ #include "catalog/pg_ts_parser.h" -+ #include "catalog/pg_ts_template.h" -+ #include "catalog/pg_type.h" -+ #include "catalog/pg_user_mapping.h" -+ #include "miscadmin.h" -+ #include "nodes/makefuncs.h" -+ #include "security/sepgsql.h" -+ #include "storage/fd.h" -+ #include "utils/fmgroids.h" -+ #include "utils/lsyscache.h" -+ #include "utils/syscache.h" -+ #include "utils/tqual.h" -+ -+ /* GUC: to turn on/off row level controls in SE-PostgreSQL */ -+ bool sepostgresql_row_level; -+ -+ /* GUC parameter to turn on/off mcstrans */ -+ bool sepostgresql_use_mcstrans; -+ -+ /* -+ * sepgsqlTupleDescHasSecid -+ * -+ * returns a hint whether we should allocate a field to store -+ * security label on the given relation, or not. -+ */ -+ bool -+ sepgsqlTupleDescHasSecid(Oid relid, char relkind) -+ { -+ if (!sepgsqlIsEnabled()) -+ return false; -+ -+ if (!OidIsValid(relid)) -+ return sepostgresql_row_level; /* Target of SELECT INTO */ -+ -+ /* These system catalogs always have its secid */ -+ if (relid == DatabaseRelationId || -+ relid == NamespaceRelationId || -+ relid == RelationRelationId || -+ relid == AttributeRelationId || -+ relid == ProcedureRelationId) -+ return true; -+ -+ /* These system catalogs are an external attributes */ -+ if (relid == AggregateRelationId || -+ relid == AccessMethodOperatorRelationId || -+ relid == AccessMethodProcedureRelationId || -+ relid == AttrDefaultRelationId || -+ relid == AuthMemRelationId || -+ relid == ConstraintRelationId || -+ relid == DescriptionRelationId || -+ relid == EnumRelationId || -+ relid == IndexRelationId || -+ relid == InheritsRelationId || -+ relid == RewriteRelationId || -+ relid == SecurityRelationId || -+ relid == SharedDescriptionRelationId || -+ relid == StatisticRelationId || -+ relid == TriggerRelationId) -+ return false; -+ -+ return sepostgresql_row_level; -+ } -+ -+ /* -+ * sepgsqlGetDefaultDatabaseSecid -+ * It returns the default security label of a database object. -+ */ -+ sepgsql_sid_t -+ sepgsqlGetDefaultDatabaseSecid(void) -+ { -+ security_context_t seclabel; -+ sepgsql_sid_t sid; -+ char filename[MAXPGPATH]; -+ char buffer[1024], *policy_type, *tmp; -+ FILE *filp; -+ -+ /* -+ * NOTE: when the security policy provide a configuration to -+ * specify the default security context of database object, -+ * we apply is as a default one. -+ * If the configuration is unavailable, we compute the -+ * default security context without any parent object. -+ */ -+ if (selinux_getpolicytype(&policy_type) < 0) -+ goto fallback; -+ -+ snprintf(filename, sizeof(filename), -+ "%s%s/contexts/sepgsql_context", selinux_path(), policy_type); -+ filp = AllocateFile(filename, PG_BINARY_R); -+ if (!filp) -+ goto fallback; -+ -+ while (fgets(buffer, sizeof(buffer), filp) != NULL) -+ { -+ tmp = strchr(buffer, '#'); -+ if (tmp) -+ *tmp = '\0'; -+ -+ seclabel = strtok(buffer, " \t\n\r"); -+ if (!seclabel) -+ continue; -+ -+ /* An entry found */ -+ FreeFile(filp); -+ -+ sid.relid = DatabaseRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, seclabel); -+ -+ return sid; -+ } -+ FreeFile(filp); -+ -+ fallback: -+ seclabel = sepgsqlComputeCreate(sepgsqlGetClientLabel(), -+ sepgsqlGetClientLabel(), -+ SEPG_CLASS_DB_DATABASE); -+ sid.relid = DatabaseRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, seclabel); -+ -+ return sid; -+ } -+ -+ static sepgsql_sid_t -+ defaultSecidWithDatabase(Oid relid, Oid datoid, uint16 tclass) -+ { -+ HeapTuple tuple; -+ sepgsql_sid_t datsid; -+ -+ if (IsBootstrapProcessingMode()) -+ { -+ static sepgsql_sid_t cached = { InvalidOid, InvalidOid }; -+ -+ if (!SidIsValid(cached)) -+ cached = sepgsqlGetDefaultDatabaseSecid(); -+ datsid = cached; -+ } -+ else -+ { -+ tuple = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(datoid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for database: %u", datoid); -+ datsid.relid = DatabaseRelationId; -+ datsid.secid = HeapTupleGetSecid(tuple); -+ ReleaseSysCache(tuple); -+ } -+ -+ return sepgsqlClientCreateSecid(datsid, tclass, relid); -+ } -+ -+ sepgsql_sid_t -+ sepgsqlGetDefaultSchemaSecid(Oid database_oid) -+ { -+ return defaultSecidWithDatabase(NamespaceRelationId, -+ database_oid, -+ SEPG_CLASS_DB_SCHEMA); -+ } -+ -+ static sepgsql_sid_t -+ defaultSecidWithSchema(Oid relid, Oid nspoid, uint16 tclass) -+ { -+ HeapTuple tuple; -+ sepgsql_sid_t nspsid; -+ -+ if (IsBootstrapProcessingMode()) -+ { -+ static sepgsql_sid_t cached = { InvalidOid, InvalidOid }; -+ -+ if (!SidIsValid(cached)) -+ cached = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); -+ -+ nspsid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); -+ } -+ else -+ { -+ tuple = SearchSysCache(NAMESPACEOID, -+ ObjectIdGetDatum(nspoid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for namespace: %u", nspoid); -+ nspsid.relid = NamespaceRelationId; -+ nspsid.secid = HeapTupleGetSecid(tuple); -+ ReleaseSysCache(tuple); -+ } -+ -+ return sepgsqlClientCreateSecid(nspsid, tclass, relid); -+ } -+ -+ sepgsql_sid_t -+ sepgsqlGetDefaultTableSecid(Oid namespace_oid) -+ { -+ return defaultSecidWithSchema(RelationRelationId, -+ namespace_oid, -+ SEPG_CLASS_DB_TABLE); -+ } -+ -+ sepgsql_sid_t -+ sepgsqlGetDefaultSequenceSecid(Oid namespace_oid) -+ { -+ return defaultSecidWithSchema(RelationRelationId, -+ namespace_oid, -+ SEPG_CLASS_DB_SEQUENCE); -+ } -+ -+ sepgsql_sid_t -+ sepgsqlGetDefaultProcedureSecid(Oid namespace_oid) -+ { -+ return defaultSecidWithSchema(ProcedureRelationId, -+ namespace_oid, -+ SEPG_CLASS_DB_PROCEDURE); -+ } -+ -+ static sepgsql_sid_t -+ defaultSecidWithTable(Oid relid, Oid tbloid, security_class_t tclass) -+ { -+ HeapTuple tuple; -+ sepgsql_sid_t relsid; -+ -+ if (IsBootstrapProcessingMode() -+ && (tbloid == TypeRelationId || -+ tbloid == ProcedureRelationId || -+ tbloid == AttributeRelationId || -+ tbloid == RelationRelationId)) -+ { -+ static sepgsql_sid_t cached = { InvalidOid, InvalidOid }; -+ -+ if (!SidIsValid(cached)) -+ cached = sepgsqlGetDefaultTableSecid(PG_CATALOG_NAMESPACE); -+ -+ relsid = cached; -+ } -+ else -+ { -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(tbloid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation: %u", tbloid); -+ relsid.relid = RelationRelationId; -+ relsid.secid = HeapTupleGetSecid(tuple); -+ ReleaseSysCache(tuple); -+ } -+ -+ return sepgsqlClientCreateSecid(relsid, tclass, relid); -+ } -+ -+ sepgsql_sid_t -+ sepgsqlGetDefaultColumnSecid(Oid table_oid) -+ { -+ return defaultSecidWithTable(AttributeRelationId, -+ table_oid, -+ SEPG_CLASS_DB_COLUMN); -+ } -+ -+ sepgsql_sid_t -+ sepgsqlGetDefaultTupleSecid(Oid table_oid) -+ { -+ return defaultSecidWithTable(table_oid, -+ table_oid, -+ SEPG_CLASS_DB_TUPLE); -+ } -+ -+ sepgsql_sid_t -+ sepgsqlGetDefaultBlobSecid(Oid database_oid) -+ { -+ return defaultSecidWithDatabase(LargeObjectRelationId, -+ MyDatabaseId, -+ SEPG_CLASS_DB_BLOB); -+ } -+ -+ void -+ sepgsqlSetDefaultSecid(Relation rel, HeapTuple tuple) -+ { -+ sepgsql_sid_t newSid; -+ Oid relOid = RelationGetRelid(rel); -+ Oid nspOid, tblOid; -+ char relkind; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ if (!HeapTupleHasSecid(tuple)) -+ return; -+ -+ /* initialize */ -+ newSid.relid = relOid; -+ newSid.secid = InvalidOid; -+ -+ switch (relOid) -+ { -+ case DatabaseRelationId: -+ newSid = sepgsqlGetDefaultDatabaseSecid(); -+ break; -+ -+ case NamespaceRelationId: -+ newSid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); -+ break; -+ -+ case RelationRelationId: -+ nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace; -+ relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind; -+ -+ switch (relkind) -+ { -+ case RELKIND_RELATION: -+ newSid = sepgsqlGetDefaultTableSecid(nspOid); -+ break; -+ -+ case RELKIND_SEQUENCE: -+ newSid = sepgsqlGetDefaultSequenceSecid(nspOid); -+ break; -+ -+ default: -+ newSid = sepgsqlGetDefaultTupleSecid(relOid); -+ break; -+ } -+ break; -+ -+ case ProcedureRelationId: -+ nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; -+ newSid = sepgsqlGetDefaultProcedureSecid(nspOid); -+ break; -+ -+ case AttributeRelationId: -+ tblOid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; -+ -+ /* -+ * We cannot refer system cache in the very early initdb -+ * phase, because pg_class is not constructed yet. -+ */ -+ if ((IsBootstrapProcessingMode() && -+ (tblOid == TypeRelationId || -+ tblOid == ProcedureRelationId || -+ tblOid == AttributeRelationId || -+ tblOid == RelationRelationId)) -+ || get_rel_relkind(tblOid) == RELKIND_RELATION) -+ { -+ newSid = sepgsqlGetDefaultColumnSecid(tblOid); -+ } -+ /* otherwise, it does not have individual security context */ -+ break; -+ -+ case LargeObjectRelationId: -+ newSid = sepgsqlGetDefaultBlobSecid(MyDatabaseId); -+ break; -+ -+ default: -+ newSid = sepgsqlGetDefaultTupleSecid(relOid); -+ break; -+ } -+ -+ HeapTupleSetSecid(tuple, newSid.secid); -+ } -+ -+ /* -+ * sepgsqlGetSysobjSecid -+ * -+ * It returns a pair of relid/secid for the given OID. -+ */ -+ static sepgsql_sid_t -+ getSysobjSecidDirect(Oid classOid, Oid indexOid, Oid objectId, uint16 *tclass) -+ { -+ sepgsql_sid_t sid; -+ Relation rel; -+ HeapTuple tup; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ -+ rel = heap_open(CastRelationId, AccessShareLock); -+ -+ ScanKeyInit(&skey, -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(objectId)); -+ -+ scan = systable_beginscan(rel, CastOidIndexId, true, -+ SnapshotNow, 1, &skey); -+ tup = systable_getnext(scan); -+ -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "system object lookup failed for oid %u on relation %u", -+ objectId, classOid); -+ -+ sid = sepgsqlGetTupleSecid(classOid, tup, tclass); -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, AccessShareLock); -+ -+ return sid; -+ } -+ -+ sepgsql_sid_t -+ sepgsqlGetSysobjSecid(Oid classOid, Oid objectId, int32 objsubId, uint16 *tclass) -+ { -+ sepgsql_sid_t sid; -+ HeapTuple tup; -+ -+ switch (classOid) -+ { -+ case AccessMethodRelationId: -+ tup = SearchSysCache(AMOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for access method: %u", objectId); -+ break; -+ -+ case AccessMethodOperatorRelationId: -+ return getSysobjSecidDirect(AccessMethodOperatorRelationId, -+ AccessMethodOperatorOidIndexId, -+ objectId, tclass); -+ -+ case AccessMethodProcedureRelationId: -+ return getSysobjSecidDirect(AccessMethodProcedureRelationId, -+ AccessMethodProcedureOidIndexId, -+ objectId, tclass); -+ -+ case AuthIdRelationId: -+ tup = SearchSysCache(AUTHOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for role: %u", objectId); -+ break; -+ -+ case CastRelationId: -+ return getSysobjSecidDirect(CastRelationId, -+ CastOidIndexId, -+ objectId, tclass); -+ -+ case ConstraintRelationId: -+ tup = SearchSysCache(CONSTROID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for constraint: %u", objectId); -+ break; -+ -+ case ConversionRelationId: -+ tup = SearchSysCache(CONVOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for conversion: %u", objectId); -+ break; -+ -+ case DatabaseRelationId: -+ tup = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for database: %u", objectId); -+ break; -+ -+ case ForeignDataWrapperRelationId: -+ tup = SearchSysCache(FOREIGNDATAWRAPPEROID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for FDW: %u", objectId); -+ break; -+ -+ case ForeignServerRelationId: -+ tup = SearchSysCache(FOREIGNSERVEROID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for foreign server: %u", objectId); -+ break; -+ -+ case LanguageRelationId: -+ tup = SearchSysCache(LANGOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ break; -+ -+ case LargeObjectRelationId: -+ /* to be replaced by LargeObjectMetaRelationId! */ -+ { -+ Relation rel; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ -+ rel = heap_open(LargeObjectRelationId, AccessShareLock); -+ -+ ScanKeyInit(&skey, -+ Anum_pg_largeobject_loid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(objectId)); -+ -+ scan = systable_beginscan(rel, LargeObjectLOidPNIndexId, -+ true, SnapshotNow, 1, &skey); -+ -+ tup = systable_getnext(scan); -+ -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "largeobject %u lookup failed", objectId); -+ -+ sid = sepgsqlGetTupleSecid(classOid, tup, tclass); -+ systable_endscan(scan); -+ -+ heap_close(rel, AccessShareLock); -+ } -+ return sid; -+ -+ case RelationRelationId: -+ if (objsubId != 0) -+ { -+ classOid = AttributeRelationId; -+ tup = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(objectId), -+ Int16GetDatum(objsubId), -+ 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for attribute %d of relation %u", -+ objsubId, objectId); -+ } -+ else -+ { -+ classOid = RelationRelationId; -+ tup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for relation %u", objectId); -+ } -+ break; -+ -+ case NamespaceRelationId: -+ tup = SearchSysCache(NAMESPACEOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for schema %u", objectId); -+ break; -+ -+ case OperatorClassRelationId: -+ tup = SearchSysCache(CLAOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for opclass %u", objectId); -+ break; -+ -+ case OperatorFamilyRelationId: -+ tup = SearchSysCache(OPFAMILYOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for opfamily %u", objectId); -+ break; -+ -+ case OperatorRelationId: -+ tup = SearchSysCache(OPEROID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for operator %u", objectId); -+ break; -+ -+ case ProcedureRelationId: -+ tup = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for procedure %u", objectId); -+ break; -+ -+ case RewriteRelationId: -+ return getSysobjSecidDirect(RewriteRelationId, -+ RewriteOidIndexId, -+ objectId, tclass); -+ -+ case TableSpaceRelationId: -+ return getSysobjSecidDirect(TableSpaceRelationId, -+ TablespaceOidIndexId, -+ objectId, tclass); -+ -+ case TriggerRelationId: -+ return getSysobjSecidDirect(TriggerRelationId, -+ TriggerOidIndexId, -+ objectId, tclass); -+ -+ case TSConfigRelationId: -+ tup = SearchSysCache(TSCONFIGOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for text search configuration %u", objectId); -+ break; -+ -+ case TSDictionaryRelationId: -+ tup = SearchSysCache(TSDICTOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for text search dictionary %u", objectId); -+ break; -+ -+ case TSParserRelationId: -+ tup = SearchSysCache(TSPARSEROID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for text search parser %u", objectId); -+ break; -+ -+ case TSTemplateRelationId: -+ tup = SearchSysCache(TSTEMPLATEOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for text search template %u", objectId); -+ break; -+ -+ case TypeRelationId: -+ tup = SearchSysCache(TYPEOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for type %u", objectId); -+ break; -+ -+ case UserMappingRelationId: -+ tup = SearchSysCache(USERMAPPINGOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for user mapping %u", objectId); -+ break; -+ -+ default: -+ elog(ERROR, "unexpected class OID: %u", classOid); -+ tup = NULL; /* for compiler quiet */ -+ break; -+ } -+ -+ Assert(HeapTupleIsValid(tup)); -+ -+ sid = sepgsqlGetTupleSecid(classOid, tup, tclass); -+ -+ ReleaseSysCache(tup); -+ -+ return sid; -+ } -+ -+ /* -+ * sepgsqlGetTupleSecid -+ * -+ * It returns a pair of relid/secid for the given HeapTuple. -+ * A few system catalogs is handled as an attribute of other -+ * system objects. -+ * E.g) pg_attrdef is an attribute of a certain pg_attribute -+ */ -+ sepgsql_sid_t -+ sepgsqlGetTupleSecid(Oid tableOid, HeapTuple tuple, uint16 *tclass) -+ { -+ sepgsql_sid_t sid; -+ HeapTuple exttup; -+ Oid extid; -+ Oid extcls; -+ AttrNumber extsub; -+ -+ /* initialize (unlabeled security context) */ -+ sid.relid = tableOid; -+ sid.secid = InvalidOid; -+ if (tclass) -+ *tclass = SEPG_CLASS_DB_TUPLE; -+ -+ switch (tableOid) -+ { -+ case AggregateRelationId: -+ extid = ((Form_pg_aggregate) GETSTRUCT(tuple))->aggfnoid; -+ exttup = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case AccessMethodOperatorRelationId: -+ extid = ((Form_pg_amop) GETSTRUCT(tuple))->amopfamily; -+ exttup = SearchSysCache(OPFAMILYOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case AccessMethodProcedureRelationId: -+ extid = ((Form_pg_amproc) GETSTRUCT(tuple))->amprocfamily; -+ exttup = SearchSysCache(OPFAMILYOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case AttrDefaultRelationId: -+ extid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid; -+ extsub = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum; -+ exttup = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(extid), -+ Int16GetDatum(extsub), -+ 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(AttributeRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case AttributeRelationId: -+ extid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; -+ exttup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ char relkind = ((Form_pg_class) GETSTRUCT(exttup))->relkind; -+ -+ if (relkind == RELKIND_RELATION) -+ { -+ if (tclass) -+ *tclass = SEPG_CLASS_DB_COLUMN; -+ sid.secid = HeapTupleGetSecid(tuple); -+ } -+ else -+ sid = sepgsqlGetTupleSecid(RelationRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case AuthMemRelationId: -+ extid = ((Form_pg_auth_members) GETSTRUCT(tuple))->roleid; -+ exttup = SearchSysCache(AUTHOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(AuthIdRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case ConstraintRelationId: -+ /* CHECK constraint is an attribute of the relation */ -+ extid = ((Form_pg_constraint) GETSTRUCT(tuple))->conrelid; -+ if (OidIsValid(extid)) -+ { -+ exttup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(RelationRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ } -+ /* DOMAIN constraint is an attribute of the domain type */ -+ extid = ((Form_pg_constraint) GETSTRUCT(tuple))->contypid; -+ if (OidIsValid(extid)) -+ { -+ sid.relid = TypeRelationId; -+ exttup = SearchSysCache(TYPEOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(TypeRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ } -+ /* Database's context for global assertion */ -+ exttup = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(MyDatabaseId), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(DatabaseRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case DatabaseRelationId: -+ sid.secid = HeapTupleGetSecid(tuple); -+ if (tclass) -+ *tclass = SEPG_CLASS_DB_DATABASE; -+ break; -+ -+ case DescriptionRelationId: -+ /* recursive call */ -+ extid = ((Form_pg_description) GETSTRUCT(tuple))->objoid; -+ extcls = ((Form_pg_description) GETSTRUCT(tuple))->classoid; -+ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass); -+ -+ case EnumRelationId: -+ extid = ((Form_pg_enum) GETSTRUCT(tuple))->enumtypid; -+ exttup = SearchSysCache(TYPEOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(TypeRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case IndexRelationId: -+ extid = ((Form_pg_index) GETSTRUCT(tuple))->indrelid; -+ exttup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(RelationRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case InheritsRelationId: -+ extid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid; -+ exttup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(RelationRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case LargeObjectRelationId: -+ sid.secid = HeapTupleGetSecid(tuple); -+ if (tclass) -+ *tclass = SEPG_CLASS_DB_BLOB; -+ break; -+ -+ case NamespaceRelationId: -+ sid.secid = HeapTupleGetSecid(tuple); -+ if (tclass) -+ *tclass = SEPG_CLASS_DB_SCHEMA; -+ break; -+ -+ case ProcedureRelationId: -+ sid.secid = HeapTupleGetSecid(tuple); -+ if (tclass) -+ *tclass = SEPG_CLASS_DB_PROCEDURE; -+ break; -+ -+ case RelationRelationId: -+ sid.secid = HeapTupleGetSecid(tuple); -+ if (tclass) -+ { -+ char relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind; -+ -+ switch (relkind) -+ { -+ case RELKIND_RELATION: -+ *tclass = SEPG_CLASS_DB_TABLE; -+ break; -+ -+ case RELKIND_SEQUENCE: -+ *tclass = SEPG_CLASS_DB_SEQUENCE; -+ break; -+ -+ default: -+ *tclass = SEPG_CLASS_DB_TUPLE; -+ break; -+ } -+ } -+ break; -+ -+ case RewriteRelationId: -+ extid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class; -+ exttup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(RelationRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case SharedDescriptionRelationId: -+ /* recursive invocation */ -+ extid = ((Form_pg_shdescription) GETSTRUCT(tuple))->objoid; -+ extcls = ((Form_pg_shdescription) GETSTRUCT(tuple))->classoid; -+ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass); -+ -+ case StatisticRelationId: -+ extid = ((Form_pg_statistic) GETSTRUCT(tuple))->starelid; -+ extsub = ((Form_pg_statistic) GETSTRUCT(tuple))->staattnum; -+ exttup = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(extid), -+ Int16GetDatum(extsub), -+ 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(AttributeRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case TriggerRelationId: -+ extid = ((Form_pg_trigger) GETSTRUCT(tuple))->tgrelid; -+ exttup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(RelationRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case TSConfigMapRelationId: -+ extid = ((Form_pg_ts_config_map) GETSTRUCT(tuple))->mapcfg; -+ exttup = SearchSysCache(TSCONFIGOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(TSConfigRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ default: -+ /* No external lookups (normal case) */ -+ sid.secid = HeapTupleGetSecid(tuple); -+ break; -+ } -+ -+ return sid; -+ } -+ -+ /* -+ * sepgsqlRawSecLabelIn -+ * correctness checks for the given security context -+ */ -+ char * -+ sepgsqlRawSecLabelIn(char *seclabel) -+ { -+ if (!sepgsqlIsEnabled()) -+ return seclabel; -+ -+ if (!seclabel || security_check_context_raw(seclabel) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("Invalid security context: \"%s\"", seclabel))); -+ -+ return seclabel; -+ } -+ -+ /* -+ * sepgsqlRawSecLabelOut -+ * correctness checks for the given security context, -+ * and replace it if invalid security context -+ */ -+ char * -+ sepgsqlRawSecLabelOut(char *seclabel) -+ { -+ if (!sepgsqlIsEnabled()) -+ return seclabel; -+ -+ if (!seclabel || security_check_context_raw(seclabel) < 0) -+ { -+ security_context_t unlabeledcon; -+ -+ if (security_get_initial_context_raw("unlabeled", -+ &unlabeledcon) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("Unabled to get unlabeled security context"))); -+ PG_TRY(); -+ { -+ seclabel = pstrdup(unlabeledcon); -+ } -+ PG_CATCH(); -+ { -+ freecon(unlabeledcon); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(unlabeledcon); -+ } -+ return seclabel; -+ } -+ -+ /* -+ * sepgsqlTransSecLabelIn -+ * sepgsqlTransSecLabelOut -+ * translation between human-readable and raw format -+ */ -+ char * -+ sepgsqlTransSecLabelIn(char *seclabel) -+ { -+ security_context_t rawlabel; -+ security_context_t result; -+ -+ if (!sepgsqlIsEnabled() || -+ !sepostgresql_use_mcstrans) -+ return seclabel; -+ -+ if (selinux_trans_to_raw_context(seclabel, &rawlabel) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: failed to translate \"%s\"", seclabel))); -+ PG_TRY(); -+ { -+ result = pstrdup(rawlabel); -+ } -+ PG_CATCH(); -+ { -+ freecon(rawlabel); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(rawlabel); -+ -+ return result; -+ } -+ -+ char * -+ sepgsqlTransSecLabelOut(char *seclabel) -+ { -+ security_context_t translabel; -+ security_context_t result; -+ -+ if (!sepgsqlIsEnabled() || -+ !sepostgresql_use_mcstrans) -+ return seclabel; -+ -+ if (selinux_raw_to_trans_context(seclabel, &translabel) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: failed to translate \"%s\"", seclabel))); -+ PG_TRY(); -+ { -+ result = pstrdup(translabel); -+ } -+ PG_CATCH(); -+ { -+ freecon(translabel); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(translabel); -+ -+ return result; -+ } -+ -+ char * -+ sepgsqlSysattSecLabelOut(Oid relid, HeapTuple tuple) -+ { -+ sepgsql_sid_t sid; -+ -+ sid = sepgsqlGetTupleSecid(relid, tuple, NULL); -+ -+ return securityTransSecLabelOut(sid.relid, sid.secid); -+ } -diff -Nrpc base/src/backend/security/sepgsql/misc.c sepgsql/src/backend/security/sepgsql/misc.c -*** base/src/backend/security/sepgsql/misc.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/misc.c Sun Aug 23 23:05:48 2009 -*************** -*** 0 **** ---- 1,304 ---- -+ /* -+ * src/backend/security/sepgsql/misc.c -+ * Miscellaneous facilities in SE-PostgreSQL -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+ #include "postgres.h" -+ #include "libpq/libpq-be.h" -+ #include "miscadmin.h" -+ #include "security/sepgsql.h" -+ #include "utils/builtins.h" -+ -+ static security_context_t clientLabel = NULL; -+ static security_context_t serverLabel = NULL; -+ -+ security_context_t -+ sepgsqlGetServerLabel(void) -+ { -+ if (!serverLabel) -+ { -+ if (getcon_raw(&serverLabel) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: could not get server label"))); -+ } -+ return serverLabel; -+ } -+ -+ security_context_t -+ sepgsqlGetClientLabel(void) -+ { -+ if (!clientLabel) -+ { -+ /* -+ * When the process is not invoked as a backend of client, -+ * it works as a server process and as a client process -+ * in same time. -+ */ -+ if (!MyProcPort) -+ return sepgsqlGetServerLabel(); -+ -+ /* -+ * SELinux provides getpeercon(3) which enables to obtain -+ * the security context of peer process. -+ * If MyProcPort->sock is unix domain socket, no special -+ * configuration is necessary. If it is tcp/ip socket, -+ * labeled IPsec or fallback context to be configured. -+ */ -+ if (getpeercon_raw(MyProcPort->sock, &clientLabel) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: could not obtain client label"))); -+ } -+ -+ return clientLabel; -+ } -+ -+ security_context_t -+ sepgsqlSwitchClient(security_context_t new_client) -+ { -+ char *old_client = sepgsqlGetClientLabel(); -+ -+ clientLabel = new_client; -+ -+ PG_TRY(); -+ { -+ sepgsqlAvcSwitchClient(clientLabel); -+ } -+ PG_CATCH(); -+ { -+ clientLabel = old_client; -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ -+ return old_client; -+ } -+ -+ /* -+ * sepgsqlIsEnabled() -+ * -+ * returns the state of SE-PostgreSQL whether enabled, or not. -+ * When functions under src/backend/utils/ are invoked, they have to -+ * be checked on the head. -+ * This status is decided with two factors. The one is GUC parameter -+ * of "sepostgresql=on/off", and the other is is_selinux_enabled(). -+ * Both of them have to be true, when SE-PostgreSQL is activated. -+ */ -+ bool sepostgresql_is_enabled; /* default is false */ -+ -+ bool -+ sepgsqlIsEnabled(void) -+ { -+ static int enabled = -1; /* unchecked */ -+ -+ if (!sepostgresql_is_enabled) -+ return false; -+ -+ if (enabled < 0) -+ enabled = is_selinux_enabled(); -+ -+ return enabled > 0 ? true : false; -+ } -+ -+ /* -+ * SE-PostgreSQL specific functions -+ */ -+ Datum -+ sepgsql_getcon(PG_FUNCTION_ARGS) -+ { -+ security_context_t context; -+ -+ if (!sepgsqlIsEnabled()) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: disabled now"))); -+ -+ context = sepgsqlGetClientLabel(); -+ context = sepgsqlTransSecLabelOut(context); -+ return CStringGetTextDatum(context); -+ } -+ -+ Datum -+ sepgsql_server_getcon(PG_FUNCTION_ARGS) -+ { -+ security_context_t context; -+ -+ if (!sepgsqlIsEnabled()) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: disabled now"))); -+ -+ context = sepgsqlGetServerLabel(); -+ context = sepgsqlTransSecLabelOut(context); -+ return CStringGetTextDatum(context); -+ } -+ -+ /* -+ * sepgsql_(get|set)_(user|role|type|range) -+ * get/set a component of security context. -+ */ -+ static void -+ parse_security_context(security_context_t context, -+ char **user, char **role, char **type, char **range) -+ { -+ security_context_t raw_context; -+ char *tok; -+ -+ if (!sepgsqlIsEnabled()) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: disabled now"))); -+ -+ if (selinux_trans_to_raw_context(context, &raw_context) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: could not translate mls label: %s", context))); -+ -+ PG_TRY(); -+ { -+ tok = strtok(raw_context, ":"); -+ if (user) -+ *user = (!tok ? NULL : pstrdup(tok)); -+ -+ tok = strtok(NULL, ":"); -+ if (role) -+ *role = (!tok ? NULL : pstrdup(tok)); -+ -+ tok = strtok(NULL, ":"); -+ if (type) -+ *type = (!tok ? NULL : pstrdup(tok)); -+ -+ tok = strtok(NULL, "\0"); -+ if (range) -+ *range = (!tok ? NULL : pstrdup(tok)); -+ } -+ PG_CATCH(); -+ { -+ freecon(raw_context); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(raw_context); -+ } -+ -+ Datum -+ sepgsql_get_user(PG_FUNCTION_ARGS) -+ { -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *user; -+ -+ parse_security_context(context, &user, NULL, NULL, NULL); -+ if (!user) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: could not extract user of \"%s\"", context))); -+ -+ PG_RETURN_TEXT_P(CStringGetTextDatum(user)); -+ } -+ -+ Datum -+ sepgsql_get_role(PG_FUNCTION_ARGS) -+ { -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *role; -+ -+ parse_security_context(context, NULL, &role, NULL, NULL); -+ if (!role) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: could not extract role of \"%s\"", context))); -+ -+ PG_RETURN_TEXT_P(CStringGetTextDatum(role)); -+ } -+ -+ Datum -+ sepgsql_get_type(PG_FUNCTION_ARGS) -+ { -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *type; -+ -+ parse_security_context(context, NULL, NULL, &type, NULL); -+ if (!type) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: could not extract type of \"%s\"", context))); -+ -+ PG_RETURN_TEXT_P(CStringGetTextDatum(type)); -+ } -+ -+ Datum -+ sepgsql_get_range(PG_FUNCTION_ARGS) -+ { -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *range; -+ -+ parse_security_context(context, NULL, NULL, NULL, &range); -+ if (!range) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: could not extract range of \"%s\"", context))); -+ -+ PG_RETURN_TEXT_P(CStringGetTextDatum(range)); -+ } -+ -+ static Datum -+ sepgsql_set_common(char *context, char *user, char *role, char *type, char *range) -+ { -+ StringInfoData newcon; -+ -+ parse_security_context(context, -+ !user ? &user : NULL, -+ !role ? &role : NULL, -+ !type ? &type : NULL, -+ !range ? &range : NULL); -+ if (!user || !role || !type) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: invalid security context: \"%s\"", context))); -+ -+ initStringInfo(&newcon); -+ appendStringInfo(&newcon, "%s:%s:%s", user, role, type); -+ if (range) -+ appendStringInfo(&newcon, ":%s", range); -+ -+ return CStringGetTextDatum(sepgsqlTransSecLabelOut(newcon.data)); -+ } -+ -+ Datum -+ sepgsql_set_user(PG_FUNCTION_ARGS) -+ { -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *user = TextDatumGetCString(PG_GETARG_TEXT_P(1)); -+ -+ return sepgsql_set_common(context, user, NULL, NULL, NULL); -+ } -+ -+ Datum -+ sepgsql_set_role(PG_FUNCTION_ARGS) -+ { -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *role = TextDatumGetCString(PG_GETARG_TEXT_P(1)); -+ -+ return sepgsql_set_common(context, NULL, role, NULL, NULL); -+ } -+ -+ Datum -+ sepgsql_set_type(PG_FUNCTION_ARGS) -+ { -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *type = TextDatumGetCString(PG_GETARG_TEXT_P(1)); -+ -+ return sepgsql_set_common(context, NULL, NULL, type, NULL); -+ } -+ -+ Datum -+ sepgsql_set_range(PG_FUNCTION_ARGS) -+ { -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *range = TextDatumGetCString(PG_GETARG_TEXT_P(1)); -+ -+ return sepgsql_set_common(context, NULL, NULL, NULL, range); -+ } -diff -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/security/sepgsql/perms.c -*** base/src/backend/security/sepgsql/perms.c Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/perms.c Mon Sep 28 09:29:32 2009 -*************** -*** 0 **** ---- 1,597 ---- -+ /* -+ * src/backend/utils/sepgsql/perms.c -+ * SE-PostgreSQL permission checks -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+ #include "postgres.h" -+ -+ #include "catalog/pg_database.h" -+ #include "catalog/pg_proc.h" -+ #include "catalog/pg_largeobject.h" -+ #include "catalog/pg_namespace.h" -+ #include "catalog/pg_type.h" -+ #include "miscadmin.h" -+ #include "security/sepgsql.h" -+ #include "utils/lsyscache.h" -+ -+ /* -+ * Dynamic object class/permissions mapping -+ * -+ * SELinux exports the list of object classes and permissions at -+ * /selinux/class. The libselinux provides an interface to translate -+ * between their names and codes. -+ */ -+ static struct -+ { -+ const char *class_name; -+ security_class_t class_code; -+ struct -+ { -+ const char *perm_name; -+ access_vector_t perm_code; -+ } av[sizeof(access_vector_t) * 8]; -+ } selinux_catalog[] = { -+ { -+ "process", SEPG_CLASS_PROCESS, -+ { -+ {"translation", SEPG_PROCESS__TRANSITION }, -+ {NULL, 0} -+ } -+ }, -+ { -+ "file", SEPG_CLASS_FILE, -+ { -+ {"read", SEPG_FILE__READ }, -+ {"write", SEPG_FILE__WRITE }, -+ {"create", SEPG_FILE__CREATE }, -+ {"getattr", SEPG_FILE__GETATTR }, -+ {NULL, 0} -+ } -+ }, -+ { -+ "dir", SEPG_CLASS_DIR, -+ { -+ {"read", SEPG_DIR__READ }, -+ {"write", SEPG_DIR__WRITE }, -+ {"create", SEPG_DIR__CREATE }, -+ {"getattr", SEPG_DIR__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "lnk_file", SEPG_CLASS_LNK_FILE, -+ { -+ {"read", SEPG_LNK_FILE__READ }, -+ {"write", SEPG_LNK_FILE__WRITE }, -+ {"create", SEPG_LNK_FILE__CREATE }, -+ {"getattr", SEPG_LNK_FILE__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "chr_file", SEPG_CLASS_CHR_FILE, -+ { -+ {"read", SEPG_CHR_FILE__READ }, -+ {"write", SEPG_CHR_FILE__WRITE }, -+ {"create", SEPG_CHR_FILE__CREATE }, -+ {"getattr", SEPG_CHR_FILE__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "blk_file", SEPG_CLASS_BLK_FILE, -+ { -+ {"read", SEPG_BLK_FILE__READ }, -+ {"write", SEPG_BLK_FILE__WRITE }, -+ {"create", SEPG_BLK_FILE__CREATE }, -+ {"getattr", SEPG_BLK_FILE__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "sock_file", SEPG_CLASS_SOCK_FILE, -+ { -+ {"read", SEPG_SOCK_FILE__READ }, -+ {"write", SEPG_SOCK_FILE__WRITE }, -+ {"create", SEPG_SOCK_FILE__CREATE }, -+ {"getattr", SEPG_SOCK_FILE__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "fifo_file", SEPG_CLASS_FIFO_FILE, -+ { -+ {"read", SEPG_FIFO_FILE__READ }, -+ {"write", SEPG_FIFO_FILE__WRITE }, -+ {"create", SEPG_FIFO_FILE__CREATE }, -+ {"getattr", SEPG_FIFO_FILE__GETATTR }, -+ {NULL, 0UL } -+ } -+ }, -+ { -+ "db_database", SEPG_CLASS_DB_DATABASE, -+ { -+ { "create", SEPG_DB_DATABASE__CREATE }, -+ { "drop", SEPG_DB_DATABASE__DROP }, -+ { "getattr", SEPG_DB_DATABASE__GETATTR }, -+ { "setattr", SEPG_DB_DATABASE__SETATTR }, -+ { "relabelfrom", SEPG_DB_DATABASE__RELABELFROM }, -+ { "relabelto", SEPG_DB_DATABASE__RELABELTO }, -+ { "access", SEPG_DB_DATABASE__ACCESS }, -+ { "install_module", SEPG_DB_DATABASE__INSTALL_MODULE }, -+ { "load_module", SEPG_DB_DATABASE__LOAD_MODULE }, -+ { "superuser", SEPG_DB_DATABASE__SUPERUSER }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_schema", SEPG_CLASS_DB_SCHEMA, -+ { -+ { "create", SEPG_DB_SCHEMA__CREATE }, -+ { "drop", SEPG_DB_SCHEMA__DROP }, -+ { "getattr", SEPG_DB_SCHEMA__GETATTR }, -+ { "setattr", SEPG_DB_SCHEMA__SETATTR }, -+ { "relabelfrom", SEPG_DB_SCHEMA__RELABELFROM }, -+ { "relabelto", SEPG_DB_SCHEMA__RELABELTO }, -+ { "search", SEPG_DB_SCHEMA__SEARCH }, -+ { "add_name", SEPG_DB_SCHEMA__ADD_NAME }, -+ { "remove_name", SEPG_DB_SCHEMA__REMOVE_NAME }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_table", SEPG_CLASS_DB_TABLE, -+ { -+ { "create", SEPG_DB_TABLE__CREATE }, -+ { "drop", SEPG_DB_TABLE__DROP }, -+ { "getattr", SEPG_DB_TABLE__GETATTR }, -+ { "setattr", SEPG_DB_TABLE__SETATTR }, -+ { "relabelfrom", SEPG_DB_TABLE__RELABELFROM }, -+ { "relabelto", SEPG_DB_TABLE__RELABELTO }, -+ { "select", SEPG_DB_TABLE__SELECT }, -+ { "update", SEPG_DB_TABLE__UPDATE }, -+ { "insert", SEPG_DB_TABLE__INSERT }, -+ { "delete", SEPG_DB_TABLE__DELETE }, -+ { "lock", SEPG_DB_TABLE__LOCK }, -+ { "reference", SEPG_DB_TABLE__REFERENCE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_sequence", SEPG_CLASS_DB_SEQUENCE, -+ { -+ { "create", SEPG_DB_SEQUENCE__CREATE }, -+ { "drop", SEPG_DB_SEQUENCE__DROP }, -+ { "getattr", SEPG_DB_SEQUENCE__GETATTR }, -+ { "setattr", SEPG_DB_SEQUENCE__SETATTR }, -+ { "relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM }, -+ { "relabelto", SEPG_DB_SEQUENCE__RELABELTO }, -+ { "get_value", SEPG_DB_SEQUENCE__GET_VALUE }, -+ { "next_value", SEPG_DB_SEQUENCE__NEXT_VALUE }, -+ { "set_value", SEPG_DB_SEQUENCE__SET_VALUE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_procedure", SEPG_CLASS_DB_PROCEDURE, -+ { -+ { "create", SEPG_DB_PROCEDURE__CREATE }, -+ { "drop", SEPG_DB_PROCEDURE__DROP }, -+ { "getattr", SEPG_DB_PROCEDURE__GETATTR }, -+ { "setattr", SEPG_DB_PROCEDURE__SETATTR }, -+ { "relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM }, -+ { "relabelto", SEPG_DB_PROCEDURE__RELABELTO }, -+ { "execute", SEPG_DB_PROCEDURE__EXECUTE }, -+ { "entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT }, -+ { "install", SEPG_DB_PROCEDURE__INSTALL }, -+ { "untrusted", SEPG_DB_PROCEDURE__UNTRUSTED }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_column", SEPG_CLASS_DB_COLUMN, -+ { -+ { "create", SEPG_DB_COLUMN__CREATE }, -+ { "drop", SEPG_DB_COLUMN__DROP }, -+ { "getattr", SEPG_DB_COLUMN__GETATTR }, -+ { "setattr", SEPG_DB_COLUMN__SETATTR }, -+ { "relabelfrom", SEPG_DB_COLUMN__RELABELFROM }, -+ { "relabelto", SEPG_DB_COLUMN__RELABELTO }, -+ { "select", SEPG_DB_COLUMN__SELECT }, -+ { "update", SEPG_DB_COLUMN__UPDATE }, -+ { "insert", SEPG_DB_COLUMN__INSERT }, -+ { "reference", SEPG_DB_COLUMN__REFERENCE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_tuple", SEPG_CLASS_DB_TUPLE, -+ { -+ { "relabelfrom", SEPG_DB_TUPLE__RELABELFROM }, -+ { "relabelto", SEPG_DB_TUPLE__RELABELTO }, -+ { "select", SEPG_DB_TUPLE__SELECT }, -+ { "update", SEPG_DB_TUPLE__UPDATE }, -+ { "insert", SEPG_DB_TUPLE__INSERT }, -+ { "delete", SEPG_DB_TUPLE__DELETE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_blob", SEPG_CLASS_DB_BLOB, -+ { -+ { "create", SEPG_DB_BLOB__CREATE }, -+ { "drop", SEPG_DB_BLOB__DROP }, -+ { "getattr", SEPG_DB_BLOB__GETATTR }, -+ { "setattr", SEPG_DB_BLOB__SETATTR }, -+ { "relabelfrom", SEPG_DB_BLOB__RELABELFROM }, -+ { "relabelto", SEPG_DB_BLOB__RELABELTO }, -+ { "read", SEPG_DB_BLOB__READ }, -+ { "write", SEPG_DB_BLOB__WRITE }, -+ { "import", SEPG_DB_BLOB__IMPORT }, -+ { "export", SEPG_DB_BLOB__EXPORT }, -+ { NULL, 0UL }, -+ } -+ } -+ }; -+ -+ /* -+ * sepgsqlTransToExternalClass -+ * It translate the given class code (defined as SEPGCLASS_(class)) into -+ * external code which is necessary to communicate in-kernel SELinux -+ */ -+ extern security_class_t -+ sepgsqlTransToExternalClass(uint16 tclass) -+ { -+ Assert(tclass < SEPG_CLASS_MAX); -+ -+ return string_to_security_class(selinux_catalog[tclass].class_name); -+ } -+ -+ /* -+ * sepgsqlTransToInternalPerms -+ * It translate the given permission masks into internal representation -+ * defined as SEPG_(class)_(permission). -+ */ -+ extern void -+ sepgsqlTransToInternalPerms(security_class_t tclass, struct av_decision *avd) -+ { -+ security_class_t tclass_ex; -+ struct av_decision i_avd; -+ int i, deny_unknown; -+ -+ Assert(tclass < SEPG_CLASS_MAX); -+ -+ memset(&i_avd, 0, sizeof(struct av_decision)); -+ -+ deny_unknown = security_deny_unknown(); -+ -+ tclass_ex = sepgsqlTransToExternalClass(tclass); -+ for (i=0; selinux_catalog[tclass].av[i].perm_name; i++) -+ { -+ const char *perm_name = selinux_catalog[tclass].av[i].perm_name; -+ access_vector_t perm_code = selinux_catalog[tclass].av[i].perm_code; -+ access_vector_t perm_code_ex; -+ -+ perm_code_ex = string_to_av_perm(tclass_ex, perm_name); -+ if (!perm_code_ex) -+ { -+ /* fill up undefined permission */ -+ if (!deny_unknown) -+ i_avd.allowed |= perm_code; -+ i_avd.decided |= perm_code; -+ i_avd.auditdeny |= perm_code; -+ continue; -+ } -+ -+ if (avd->allowed & perm_code_ex) -+ i_avd.allowed |= perm_code; -+ if (avd->decided & perm_code_ex) -+ i_avd.decided |= perm_code; -+ if (avd->auditallow & perm_code_ex) -+ i_avd.auditallow |= perm_code; -+ if (avd->auditdeny & perm_code_ex) -+ i_avd.auditdeny |= perm_code; -+ } -+ -+ avd->allowed = i_avd.allowed; -+ avd->decided = i_avd.decided; -+ avd->auditallow = i_avd.auditallow; -+ avd->auditdeny = i_avd.auditdeny; -+ } -+ -+ /* -+ * sepgsqlGetClassString -+ * sepgsqlGetPermissionString -+ * It returns text representation of object classes/permissions -+ */ -+ const char * -+ sepgsqlGetClassString(uint16 tclass) -+ { -+ Assert(tclass < SEPG_CLASS_MAX); -+ -+ return selinux_catalog[tclass].class_name; -+ } -+ -+ const char * -+ sepgsqlGetPermString(uint16 tclass, uint32 permission) -+ { -+ int i; -+ -+ Assert(tclass < SEPG_CLASS_MAX); -+ -+ for (i=0; selinux_catalog[tclass].av[i].perm_name; i++) -+ { -+ if (selinux_catalog[tclass].av[i].perm_code == permission) -+ return selinux_catalog[tclass].av[i].perm_name; -+ } -+ return NULL; -+ } -+ -+ #if 0 -+ -+ /* -+ * sepgsqlFileObjectClass -+ * -+ * It returns proper object class of filesystem object already opened. -+ * It is necessary to check privileges voluntarily. -+ */ -+ uint16 -+ sepgsqlFileObjectClass(int fdesc) -+ { -+ struct stat stbuf; -+ -+ if (fstat(fdesc, &stbuf) != 0) -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not stat file descriptor: %d", fdesc))); -+ -+ if (S_ISDIR(stbuf.st_mode)) -+ return SEPG_CLASS_DIR; -+ else if (S_ISCHR(stbuf.st_mode)) -+ return SEPG_CLASS_CHR_FILE; -+ else if (S_ISBLK(stbuf.st_mode)) -+ return SEPG_CLASS_BLK_FILE; -+ else if (S_ISFIFO(stbuf.st_mode)) -+ return SEPG_CLASS_FIFO_FILE; -+ else if (S_ISLNK(stbuf.st_mode)) -+ return SEPG_CLASS_LNK_FILE; -+ else if (S_ISSOCK(stbuf.st_mode)) -+ return SEPG_CLASS_SOCK_FILE; -+ -+ return SEPG_CLASS_FILE; -+ } -+ -+ /* -+ * sepgsqlTupleObjectClass -+ * -+ * It returns correct object class of given tuple -+ */ -+ uint16 -+ sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple) -+ { -+ Form_pg_class clsForm; -+ Form_pg_attribute attForm; -+ -+ switch (relid) -+ { -+ case DatabaseRelationId: -+ return SEPG_CLASS_DB_DATABASE; -+ -+ case NamespaceRelationId: -+ return SEPG_CLASS_DB_SCHEMA; -+ -+ case RelationRelationId: -+ clsForm = (Form_pg_class) GETSTRUCT(tuple); -+ if (clsForm->relkind == RELKIND_RELATION) -+ return SEPG_CLASS_DB_TABLE; -+ if (clsForm->relkind == RELKIND_SEQUENCE) -+ return SEPG_CLASS_DB_SEQUENCE; -+ break; -+ -+ case AttributeRelationId: -+ attForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ if (IsBootstrapProcessingMode() && -+ (attForm->attrelid == TypeRelationId || -+ attForm->attrelid == ProcedureRelationId || -+ attForm->attrelid == AttributeRelationId || -+ attForm->attrelid == RelationRelationId)) -+ return SEPG_CLASS_DB_COLUMN; -+ -+ if (get_rel_relkind(attForm->attrelid) == RELKIND_RELATION) -+ return SEPG_CLASS_DB_COLUMN; -+ break; -+ -+ case ProcedureRelationId: -+ return SEPG_CLASS_DB_PROCEDURE; -+ -+ case LargeObjectRelationId: -+ return SEPG_CLASS_DB_BLOB; -+ } -+ return SEPG_CLASS_DB_TUPLE; -+ } -+ -+ /* -+ * sepgsqlTupleNamespace -+ * -+ * It returns an OID of the namespace, if the given system object is -+ * deployed under a certain namespace. -+ */ -+ Oid -+ sepgsqlTupleNamespace(Oid relOid, HeapTuple tuple) -+ { -+ Oid nspOid; -+ -+ switch (relOid) -+ { -+ case RelationRelationId: -+ nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace; -+ break; -+ -+ case ConstraintRelationId: -+ nspOid = ((Form_pg_constraint) GETSTRUCT(tuple))->connamespace; -+ break; -+ -+ case ConversionRelationId: -+ nspOid = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace; -+ break; -+ -+ case OperatorClassRelationId: -+ nspOid = ((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace; -+ break; -+ -+ case OperatorRelationId: -+ nspOid = ((Form_pg_operator) GETSTRUCT(tuple))->oprnamespace; -+ break; -+ -+ case OperatorFamilyRelationId: -+ nspOid = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace; -+ break; -+ -+ case ProcedureRelationId: -+ nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; -+ break; -+ -+ case TSConfigRelationId: -+ nspOid = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace; -+ break; -+ -+ case TSDictionaryRelationId: -+ nspOid = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace; -+ break; -+ -+ case TSParserRelationId: -+ nspOid = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace; -+ break; -+ -+ case TSTemplateRelationId: -+ nspOid = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace; -+ break; -+ -+ default: -+ /* no specific namespace */ -+ nspOid = InvalidOid; -+ break; -+ } -+ -+ return nspOid; -+ } -+ -+ /* -+ * sepgsqlTupleAuditName -+ * -+ * It returns an OID of the namespace, if the given system object is -+ * deployed under a certain namespace. -+ */ -+ void -+ sepgsqlTupleAuditName(Oid relid, HeapTuple tuple, char *auname_buf) -+ { -+ char *name; -+ Oid extid; -+ -+ switch (relid) -+ { -+ case AccessMethodRelationId: -+ name = NameStr(((Form_pg_am) GETSTRUCT(tuple))->amname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case AttributeRelationId: -+ name = NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname); -+ extid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; -+ sprintf(audit_name, "%s.%s", name, extid); -+ return; -+ -+ case AuthIdRelationId: -+ name = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case ConversionRelationId: -+ name = NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case DatabaseRelationId: -+ name = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case ForeignDataWrapperRelationId: -+ name = NameStr(((Form_pg_foreign_data_wrapper) GETSTRUCT(tuple))->fdwname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case ForeignServerRelationId: -+ name = NameStr(((Form_pg_foreign_server) GETSTRUCT(tuple))->srvname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case LanguageRelationId: -+ name = NameStr(((Form_pg_language) GETSTRUCT(tuple))->lanname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case NamespaceRelationId: -+ name = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case OperatorClassRelationId: -+ name = NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case OperatorRelationId: -+ name = NameStr(((Form_pg_operator) GETSTRUCT(tuple))->oprname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case OperatorFamilyRelationId: -+ name = NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case ProcedureRelationId: -+ name = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case RelationRelationId: -+ name = NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case TableSpaceRelationId: -+ name = NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case TSConfigRelationId: -+ name = NameStr(((Form_pg_ts_config) GETSTRUCT(tuple))->cfgname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case TSDictionaryRelationId: -+ name = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case TSParserRelationId: -+ name = NameStr(((Form_pg_ts_parser) GETSTRUCT(tuple))->prsname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case TSTemplateRelationId: -+ name = NameStr(((Form_pg_templace) GETSTRUCT(tuple))->tmplname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ default: -+ /* no auditable name */ -+ auname_buf[0] = '\0'; -+ break; -+ } -+ } -+ #endif -diff -Nrpc base/src/backend/security/sepgsql/policy/Makefile sepgsql/src/backend/security/sepgsql/policy/Makefile -*** base/src/backend/security/sepgsql/policy/Makefile Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/policy/Makefile Wed Jul 15 19:35:52 2009 -*************** -*** 0 **** ---- 1,28 ---- -+ # -+ # Makefile for SE-PostgreSQL security policy module -+ # -+ top_builddir = ../../../../.. -+ include $(top_builddir)/src/Makefile.global -+ -+ POLICY_BASEDIR := $(DESTDIR)/usr/share/selinux -+ POLICY_MAKEFILE := $(POLICY_BASEDIR)/devel/Makefile -+ POLICY_INSTDIR := $(POLICY_BASEDIR)/packages -+ PREFIX_RULE := "s/%%__prefix__%%/$(shell echo $(prefix)|sed 's/\//\\\//g')/g" -+ BINDIR_RULE := "s/%%__bindir__%%/$(shell echo $(bindir)|sed 's/\//\\\//g')/g" -+ LIBDIR_RULE := "s/%%__libdir__%%/$(shell echo $(pkglibdir)|sed 's/\//\\\//g')/g" -+ -+ all: sepostgresql-devel.pp -+ -+ install: all -+ test -d $(POLICY_INSTDIR) || mkdir -p $(POLICY_INSTDIR) -+ install -p -m 0644 sepostgresql-devel.pp $(POLICY_INSTDIR) -+ -+ sepostgresql-devel.pp: sepostgresql-devel.te sepostgresql-devel.fc -+ $(MAKE) -f $(POLICY_MAKEFILE) -+ -+ sepostgresql-devel.fc: sepostgresql-devel.fc.template -+ cat $< | sed -e $(PREFIX_RULE) -e $(BINDIR_RULE) -e $(LIBDIR_RULE) > $@ -+ -+ clean: -+ $(MAKE) -f $(POLICY_MAKEFILE) clean -+ rm -f *.fc -diff -Nrpc base/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template -*** base/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template Wed Jul 15 19:35:52 2009 -*************** -*** 0 **** ---- 1,12 ---- -+ # -+ # SE-PostgreSQL install path -+ # -+ %%__prefix__%%(/.*)? -- gen_context(system_u:object_r:usr_t,s0) -+ -+ %%__bindir__%%/(se)?postgres -- gen_context(system_u:object_r:postgresql_exec_t,s0) -+ %%__bindir__%%/(se)?pg_ctl -- gen_context(system_u:object_r:initrc_exec_t,s0) -+ %%__bindir__%%/initdb(\.sepgsql)? -- gen_context(system_u:object_r:postgresql_exec_t,s0) -+ %%__bindir__%%(/.*)? -- gen_context(system_u:object_r:bin_t,s0) -+ -+ %%__libdir__%%(/.*)? -- gen_context(system_u:object_r:lib_t,s0) -+ -diff -Nrpc base/src/backend/security/sepgsql/policy/sepostgresql-devel.te sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.te -*** base/src/backend/security/sepgsql/policy/sepostgresql-devel.te Thu Jan 1 09:00:00 1970 ---- sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.te Tue Dec 1 17:11:40 2009 -*************** -*** 0 **** ---- 1,123 ---- -+ policy_module(sepostgresql-devel, 3.29) -+ -+ gen_require(` -+ class db_database all_db_database_perms; -+ class db_table all_db_table_perms; -+ class db_procedure all_db_procedure_perms; -+ class db_column all_db_column_perms; -+ class db_tuple all_db_tuple_perms; -+ class db_blob all_db_blob_perms; -+ -+ attribute sepgsql_client_type; -+ attribute sepgsql_unconfined_type; -+ -+ attribute sepgsql_database_type; -+ attribute sepgsql_table_type; -+ attribute sepgsql_sysobj_table_type; -+ attribute sepgsql_procedure_type; -+ attribute sepgsql_blob_type; -+ attribute sepgsql_module_type; -+ -+ # for regression test -+ type bin_t; -+ type user_home_t; -+ type sepgsql_trusted_proc_exec_t; -+ -+ attribute tmpfile; -+ ') -+ -+ ################################# -+ # -+ # Domain for Testcases -+ # -+ -+ role sepgsql_test_r; -+ -+ userdom_unpriv_user_template(sepgsql_test) -+ postgresql_role(sepgsql_test_r, sepgsql_test_t) -+ -+ allow sepgsql_test_t tmpfile : dir search_dir_perms; -+ allow sepgsql_test_t tmpfile : file rw_file_perms; -+ -+ optional_policy(` -+ term_write_all_terms(sepgsql_test_t) -+ ') -+ -+ optional_policy(` -+ gen_require(` -+ type unconfined_t; -+ role unconfined_r; -+ ') -+ -+ tunable_policy(`sepgsql_regression_test_mode',` -+ allow unconfined_t sepgsql_test_t : process transition; -+ ') -+ unconfined_rw_pipes(sepgsql_test_t) -+ role unconfined_r types sepgsql_test_t; -+ role unconfined_r types sepgsql_trusted_proc_t; -+ ') -+ -+ ################################# -+ # -+ # SE-PostgreSQL Declarations -+ # -+ -+ ## -+ ##

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

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

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

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

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

-+ ##
-+ gen_tunable(sepgsql_regression_test_mode, false) -+ -+ ######################################## -+ # -+ # SE-PostgreSQL audit switch for debugging -+ # -+ tunable_policy(`sepgsql_enable_auditallow',` -+ auditallow domain sepgsql_database_type : db_database *; -+ auditallow domain sepgsql_table_type : db_table *; -+ auditallow domain sepgsql_table_type : db_column *; -+ auditallow domain sepgsql_table_type : db_tuple { relabelfrom relabelto }; -+ auditallow domain sepgsql_sysobj_table_type : db_tuple *; -+ auditallow domain sepgsql_procedure_type : db_procedure *; -+ auditallow domain sepgsql_blob_type : db_blob *; -+ auditallow domain sepgsql_module_type : db_database { install_module }; -+ auditallow sepgsql_database_type sepgsql_module_type : db_database { load_module }; -+ ') -+ -+ tunable_policy(`! sepgsql_enable_auditdeny',` -+ dontaudit domain sepgsql_database_type : db_database *; -+ dontaudit domain sepgsql_table_type : db_table *; -+ dontaudit domain sepgsql_table_type : db_column *; -+ dontaudit domain sepgsql_table_type : db_tuple { relabelfrom relabelto }; -+ dontaudit domain sepgsql_sysobj_table_type : db_tuple *; -+ dontaudit domain sepgsql_procedure_type : db_procedure *; -+ dontaudit domain sepgsql_blob_type : db_blob *; -+ dontaudit domain sepgsql_module_type : db_database { install_module }; -+ dontaudit sepgsql_database_type sepgsql_module_type : db_database { load_module }; -+ ') -+ -+ ######################################## -+ # -+ # SE-PostgreSQL regression test mode switch -+ # -+ tunable_policy(`sepgsql_regression_test_mode',` -+ allow sepgsql_client_type user_home_t : db_database { install_module }; -+ allow sepgsql_unconfined_type user_home_t : db_database { install_module }; -+ allow sepgsql_database_type user_home_t : db_database { load_module }; -+ ') -diff -Nrpc base/src/backend/storage/file/fd.c sepgsql/src/backend/storage/file/fd.c -*** base/src/backend/storage/file/fd.c 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 base/src/backend/storage/ipc/ipci.c sepgsql/src/backend/storage/ipc/ipci.c -*** base/src/backend/storage/ipc/ipci.c Thu May 7 08:49:32 2009 ---- sepgsql/src/backend/storage/ipc/ipci.c Wed Jul 15 19:35:52 2009 -*************** -*** 25,30 **** ---- 25,31 ---- - #include "postmaster/autovacuum.h" - #include "postmaster/bgwriter.h" - #include "postmaster/postmaster.h" -+ #include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/ipc.h" - #include "storage/pg_shmem.h" -*************** CreateSharedMemoryAndSemaphores(bool mak -*** 119,124 **** ---- 120,126 ---- - #ifdef EXEC_BACKEND - size = add_size(size, ShmemBackendArraySize()); - #endif -+ size = add_size(size, sepgsqlShmemSize()); - - /* freeze the addin request size and include it */ - addin_request_allowed = false; -diff -Nrpc base/src/backend/storage/large_object/inv_api.c sepgsql/src/backend/storage/large_object/inv_api.c -*** base/src/backend/storage/large_object/inv_api.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/storage/large_object/inv_api.c Tue Sep 8 23:55:48 2009 -*************** -*** 39,44 **** ---- 39,45 ---- - #include "catalog/pg_largeobject.h" - #include "commands/comment.h" - #include "libpq/libpq-fs.h" -+ #include "security/sepgsql.h" - #include "storage/large_object.h" - #include "utils/fmgroids.h" - #include "utils/rel.h" -*************** close_lo_relation(bool isCommit) -*** 137,148 **** - * read with can be specified. - */ - static bool -! myLargeObjectExists(Oid loid, Snapshot snapshot) - { - bool retval = false; - Relation pg_largeobject; - ScanKeyData skey[1]; - SysScanDesc sd; - - /* - * See if we can find any tuples belonging to the specified LO ---- 138,150 ---- - * read with can be specified. - */ - static bool -! myLargeObjectExists(LargeObjectDesc *lobj) - { - bool retval = false; - Relation pg_largeobject; - ScanKeyData skey[1]; - SysScanDesc sd; -+ HeapTuple tuple; - - /* - * See if we can find any tuples belonging to the specified LO -*************** myLargeObjectExists(Oid loid, Snapshot s -*** 150,164 **** - ScanKeyInit(&skey[0], - Anum_pg_largeobject_loid, - BTEqualStrategyNumber, F_OIDEQ, -! ObjectIdGetDatum(loid)); - - pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock); - - sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, -! snapshot, 1, skey); - -! if (systable_getnext(sd) != NULL) - retval = true; - - systable_endscan(sd); - ---- 152,170 ---- - ScanKeyInit(&skey[0], - Anum_pg_largeobject_loid, - BTEqualStrategyNumber, F_OIDEQ, -! ObjectIdGetDatum(lobj->id)); - - pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock); - - sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, -! lobj->snapshot, 1, skey); - -! tuple = systable_getnext(sd); -! if (HeapTupleIsValid(tuple)) -! { - retval = true; -+ lobj->secid = HeapTupleGetSecid(tuple); -+ } - - systable_endscan(sd); - -*************** inv_open(Oid lobjId, int flags, MemoryCo -*** 260,266 **** - elog(ERROR, "invalid flags: %d", flags); - - /* Can't use LargeObjectExists here because it always uses SnapshotNow */ -! if (!myLargeObjectExists(lobjId, retval->snapshot)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("large object %u does not exist", lobjId))); ---- 266,272 ---- - elog(ERROR, "invalid flags: %d", flags); - - /* Can't use LargeObjectExists here because it always uses SnapshotNow */ -! if (!myLargeObjectExists(retval)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("large object %u does not exist", lobjId))); -*************** inv_write(LargeObjectDesc *obj_desc, con -*** 642,647 **** ---- 648,655 ---- - replace[Anum_pg_largeobject_data - 1] = true; - newtup = heap_modify_tuple(oldtuple, RelationGetDescr(lo_heap_r), - values, nulls, replace); -+ if (HeapTupleHasSecid(newtup)) -+ HeapTupleSetSecid(newtup, obj_desc->secid); - simple_heap_update(lo_heap_r, &newtup->t_self, newtup); - CatalogIndexInsert(indstate, newtup); - heap_freetuple(newtup); -*************** inv_write(LargeObjectDesc *obj_desc, con -*** 685,690 **** ---- 693,700 ---- - values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno); - values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf); - newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls); -+ if (HeapTupleHasSecid(newtup)) -+ HeapTupleSetSecid(newtup, obj_desc->secid); - simple_heap_insert(lo_heap_r, newtup); - CatalogIndexInsert(indstate, newtup); - heap_freetuple(newtup); -*************** inv_truncate(LargeObjectDesc *obj_desc, -*** 845,850 **** ---- 855,862 ---- - values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno); - values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf); - newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls); -+ if (HeapTupleHasSecid(newtup)) -+ HeapTupleSetSecid(newtup, obj_desc->secid); - simple_heap_insert(lo_heap_r, newtup); - CatalogIndexInsert(indstate, newtup); - heap_freetuple(newtup); -*************** inv_truncate(LargeObjectDesc *obj_desc, -*** 868,870 **** ---- 880,978 ---- - */ - CommandCounterIncrement(); - } -+ -+ Oid -+ inv_get_security(Oid loid) -+ { -+ Relation rel; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ Oid secid = InvalidOid; -+ -+ ScanKeyInit(&skey, -+ Anum_pg_largeobject_loid, -+ BTEqualStrategyNumber, -+ F_OIDEQ, ObjectIdGetDatum(loid)); -+ -+ rel = heap_open(LargeObjectRelationId, AccessShareLock); -+ -+ scan = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, -+ SnapshotNow, 1, &skey); -+ tuple = systable_getnext(scan); -+ -+ if (HeapTupleIsValid(tuple)) -+ { -+ /* -+ * SELinux: check db_blob:{getattr} -+ */ -+ sepgsqlCheckBlobGetattr(tuple); -+ secid = HeapTupleGetSecid(tuple); -+ } -+ systable_endscan(scan); -+ -+ heap_close(rel, AccessShareLock); -+ -+ return secid; -+ } -+ -+ void -+ inv_set_security(Oid loid, Oid secid) -+ { -+ Relation rel; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ CatalogIndexState ind; -+ bool found = false; -+ -+ ScanKeyInit(&skey, -+ Anum_pg_largeobject_loid, -+ BTEqualStrategyNumber, -+ F_OIDEQ, ObjectIdGetDatum(loid)); -+ -+ rel = heap_open(LargeObjectRelationId, RowExclusiveLock); -+ -+ ind = CatalogOpenIndexes(rel); -+ -+ scan = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, -+ SnapshotNow, 1, &skey); -+ while (HeapTupleIsValid(tuple = systable_getnext(scan))) -+ { -+ HeapTuple newtuple; -+ Datum values[Natts_pg_largeobject]; -+ bool nulls[Natts_pg_largeobject]; -+ bool replaces[Natts_pg_largeobject]; -+ -+ memset(replaces, false, sizeof(replaces)); -+ -+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), -+ values, nulls, replaces); -+ if (!HeapTupleHasSecid(newtuple)) -+ elog(ERROR, "Unable to assign security label on \"%s\"", -+ RelationGetRelationName(rel)); -+ HeapTupleSetSecid(newtuple, secid); -+ -+ /* -+ * SELinux: check db_blob:{setattr relabelfrom relabelto} -+ */ -+ if (!found) -+ sepgsqlCheckBlobRelabel(tuple, newtuple); -+ -+ simple_heap_update(rel, &tuple->t_self, newtuple); -+ CatalogUpdateIndexes(rel, newtuple); -+ found = true; -+ } -+ systable_endscan(scan); -+ -+ CatalogCloseIndexes(ind); -+ -+ heap_close(rel, RowExclusiveLock); -+ -+ CommandCounterIncrement(); -+ -+ if (!found) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_OBJECT), -+ errmsg("large object %u does not exist", loid))); -+ } -diff -Nrpc base/src/backend/tcop/fastpath.c sepgsql/src/backend/tcop/fastpath.c -*** base/src/backend/tcop/fastpath.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/tcop/fastpath.c Thu Sep 17 17:04:16 2009 -*************** -*** 26,31 **** ---- 26,32 ---- - #include "libpq/pqformat.h" - #include "mb/pg_wchar.h" - #include "miscadmin.h" -+ #include "security/sepgsql.h" - #include "tcop/fastpath.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" -*************** HandleFunctionRequest(StringInfo msgBuf) -*** 343,353 **** ---- 344,356 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(fip->namespace)); -+ sepgsql_schema_search(fip->namespace, true); - - aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(fid)); -+ sepgsql_proc_execute(fid); - - /* - * Prepare function call info block and insert arguments. -diff -Nrpc base/src/backend/tcop/pquery.c sepgsql/src/backend/tcop/pquery.c -*** base/src/backend/tcop/pquery.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/tcop/pquery.c Wed Jul 15 19:30:50 2009 -*************** PortalStart(Portal portal, ParamListInfo -*** 573,579 **** - Assert(pstmt->returningLists); - portal->tupDesc = - ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), -! false); - } - - /* ---- 573,579 ---- - Assert(pstmt->returningLists); - portal->tupDesc = - ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), -! false, false); - } - - /* -diff -Nrpc base/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c -*** base/src/backend/tcop/utility.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/backend/tcop/utility.c Tue Dec 15 17:30:25 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) -*** 1661,1666 **** ---- 1668,1698 ---- - } - 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) -*** 2239,2244 **** ---- 2271,2280 ---- - lev = LOGSTMT_DDL; - break; - -+ case T_AlterSecLabelStmt: -+ lev = LOGSTMT_DDL; -+ break; -+ - case T_AlterTableStmt: - lev = LOGSTMT_DDL; - break; -diff -Nrpc base/src/backend/utils/adt/genfile.c sepgsql/src/backend/utils/adt/genfile.c -*** base/src/backend/utils/adt/genfile.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/utils/adt/genfile.c Mon Sep 28 09:29:32 2009 -*************** -*** 24,29 **** ---- 24,30 ---- - #include "funcapi.h" - #include "miscadmin.h" - #include "postmaster/syslogger.h" -+ #include "security/sepgsql.h" - #include "storage/fd.h" - #include "utils/builtins.h" - #include "utils/memutils.h" -*************** pg_read_file(PG_FUNCTION_ARGS) -*** 99,104 **** ---- 100,108 ---- - - filename = convert_and_check_filename(filename_t); - -+ /* SELinux: check file:{read} permission */ -+ sepgsql_file_read(filename); -+ - if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL) - ereport(ERROR, - (errcode_for_file_access(), -*************** pg_stat_file(PG_FUNCTION_ARGS) -*** 159,164 **** ---- 163,170 ---- - (errmsg("must be superuser to get file information")))); - - filename = convert_and_check_filename(filename_t); -+ /* SELinux: check file:{getattr} permission */ -+ sepgsql_file_stat(filename); - - if (stat(filename, &fst) < 0) - ereport(ERROR, -diff -Nrpc base/src/backend/utils/adt/ri_triggers.c sepgsql/src/backend/utils/adt/ri_triggers.c -*** base/src/backend/utils/adt/ri_triggers.c 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 base/src/backend/utils/adt/tid.c sepgsql/src/backend/utils/adt/tid.c -*** base/src/backend/utils/adt/tid.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/utils/adt/tid.c Fri Sep 18 14:51:00 2009 -*************** currtid_byreloid(PG_FUNCTION_ARGS) -*** 347,352 **** ---- 347,354 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(rel)); -+ /* SELinux checks */ -+ sepgsql_relation_get_transaction_id(RelationGetRelid(rel)); - - if (rel->rd_rel->relkind == RELKIND_VIEW) - return currtid_for_view(rel, tid); -*************** currtid_byrelname(PG_FUNCTION_ARGS) -*** 377,382 **** ---- 379,386 ---- - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(rel)); -+ /* SELinux checks */ -+ sepgsql_relation_get_transaction_id(RelationGetRelid(rel)); - - if (rel->rd_rel->relkind == RELKIND_VIEW) - return currtid_for_view(rel, tid); -diff -Nrpc base/src/backend/utils/adt/trigfuncs.c sepgsql/src/backend/utils/adt/trigfuncs.c -*** base/src/backend/utils/adt/trigfuncs.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/utils/adt/trigfuncs.c Tue Sep 8 23:55:48 2009 -*************** suppress_redundant_updates_trigger(PG_FU -*** 76,81 **** ---- 76,85 ---- - !OidIsValid(HeapTupleHeaderGetOid(newheader))) - HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader)); - -+ if (HeapTupleHeaderHasSecid(newheader) && -+ !OidIsValid(HeapTupleHeaderGetSecid(newheader))) -+ HeapTupleHeaderSetSecid(newheader, HeapTupleHeaderGetSecid(oldheader)); -+ - /* if the tuple payload is the same ... */ - if (newtuple->t_len == oldtuple->t_len && - newheader->t_hoff == oldheader->t_hoff && -diff -Nrpc base/src/backend/utils/cache/plancache.c sepgsql/src/backend/utils/cache/plancache.c -*** base/src/backend/utils/cache/plancache.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/utils/cache/plancache.c Sun Sep 6 19:53:10 2009 -*************** PlanCacheComputeResultDesc(List *stmt_li -*** 859,870 **** - if (IsA(node, Query)) - { - query = (Query *) node; -! return ExecCleanTypeFromTL(query->targetList, false); - } - if (IsA(node, PlannedStmt)) - { - pstmt = (PlannedStmt *) node; -! return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); - } - /* other cases shouldn't happen, but return NULL */ - break; ---- 859,870 ---- - if (IsA(node, Query)) - { - query = (Query *) node; -! return ExecCleanTypeFromTL(query->targetList, false, false); - } - if (IsA(node, PlannedStmt)) - { - pstmt = (PlannedStmt *) node; -! return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false); - } - /* other cases shouldn't happen, but return NULL */ - break; -*************** PlanCacheComputeResultDesc(List *stmt_li -*** 875,887 **** - { - query = (Query *) node; - Assert(query->returningList); -! return ExecCleanTypeFromTL(query->returningList, false); - } - if (IsA(node, PlannedStmt)) - { - pstmt = (PlannedStmt *) node; - Assert(pstmt->returningLists); -! return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false); - } - /* other cases shouldn't happen, but return NULL */ - break; ---- 875,888 ---- - { - query = (Query *) node; - Assert(query->returningList); -! return ExecCleanTypeFromTL(query->returningList, false, false); - } - if (IsA(node, PlannedStmt)) - { - pstmt = (PlannedStmt *) node; - Assert(pstmt->returningLists); -! return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), -! false, false); - } - /* other cases shouldn't happen, but return NULL */ - break; -diff -Nrpc base/src/backend/utils/cache/relcache.c sepgsql/src/backend/utils/cache/relcache.c -*** base/src/backend/utils/cache/relcache.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/backend/utils/cache/relcache.c Tue Dec 15 17:30:25 2009 -*************** -*** 47,52 **** ---- 47,53 ---- - #include "catalog/pg_opclass.h" - #include "catalog/pg_proc.h" - #include "catalog/pg_rewrite.h" -+ #include "catalog/pg_security.h" - #include "catalog/pg_type.h" - #include "commands/trigger.h" - #include "miscadmin.h" -*************** RelationBuildDesc(Oid targetRelId, Relat -*** 865,870 **** ---- 866,875 ---- - /* extract reloptions if any */ - RelationParseRelOptions(relation, pg_class_tuple); - -+ /* Fixup relation->rd_att->tdhassecid */ -+ RelationGetDescr(relation)->tdhassecid -+ = securityTupleDescHasSecid(relid, relp->relkind); -+ - /* - * initialize the relation lock manager information - */ -*************** formrdesc(const char *relationName, Oid -*** 1460,1465 **** ---- 1465,1475 ---- - 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 -*** 2749,2754 **** ---- 2759,2771 ---- - 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) -*** 3503,3508 **** ---- 3520,3530 ---- - rel->rd_options = NULL; - } - -+ /* Fixup rel->rd_att->tdhassecid */ -+ RelationGetDescr(rel)->tdhassecid -+ = securityTupleDescHasSecid(RelationGetRelid(rel), -+ RelationGetForm(rel)->relkind); -+ - /* mark not-null status */ - if (has_not_null) - { -diff -Nrpc base/src/backend/utils/cache/syscache.c sepgsql/src/backend/utils/cache/syscache.c -*** base/src/backend/utils/cache/syscache.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/backend/utils/cache/syscache.c Wed Sep 9 21:29:33 2009 -*************** -*** 41,46 **** ---- 41,47 ---- - #include "catalog/pg_opfamily.h" - #include "catalog/pg_proc.h" - #include "catalog/pg_rewrite.h" -+ #include "catalog/pg_security.h" - #include "catalog/pg_statistic.h" - #include "catalog/pg_ts_config.h" - #include "catalog/pg_ts_config_map.h" -*************** static const struct cachedesc cacheinfo[ -*** 584,589 **** ---- 585,614 ---- - }, - 1024 - }, -+ {SecurityRelationId, /* SECURITYATTR */ -+ SecuritySecattrIndexId, -+ Anum_pg_security_relid, -+ 4, -+ { -+ Anum_pg_security_datid, -+ Anum_pg_security_relid, -+ Anum_pg_security_seckind, -+ Anum_pg_security_secattr -+ }, -+ 128, -+ }, -+ {SecurityRelationId, /* SECURITYSECID */ -+ SecuritySecidIndexId, -+ Anum_pg_security_relid, -+ 2, -+ { -+ Anum_pg_security_secid, -+ Anum_pg_security_datid, -+ 0, -+ 0 -+ }, -+ 128 -+ }, - {StatisticRelationId, /* STATRELATT */ - StatisticRelidAttnumIndexId, - Anum_pg_statistic_starelid, -*************** GetSysCacheOid(int cacheId, -*** 931,937 **** - return result; - } - -- - /* - * SearchSysCacheAttName - * ---- 956,961 ---- -diff -Nrpc base/src/backend/utils/fmgr/dfmgr.c sepgsql/src/backend/utils/fmgr/dfmgr.c -*** base/src/backend/utils/fmgr/dfmgr.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/utils/fmgr/dfmgr.c Thu Sep 17 17:04:16 2009 -*************** -*** 23,28 **** ---- 23,29 ---- - #endif - #include "lib/stringinfo.h" - #include "miscadmin.h" -+ #include "security/sepgsql.h" - #include "utils/dynamic_loader.h" - #include "utils/hsearch.h" - -*************** load_external_function(char *filename, c -*** 109,114 **** ---- 110,118 ---- - /* Expand the possibly-abbreviated filename to an exact path name */ - fullname = expand_dynamic_library_name(filename); - -+ /* SELinux checks db_database:{load_module} */ -+ sepgsql_database_load_module(MyDatabaseId, fullname); -+ - /* Load the shared library, unless we already did */ - lib_handle = internal_load_library(fullname); - -*************** load_file(const char *filename, bool res -*** 149,154 **** ---- 153,161 ---- - /* Expand the possibly-abbreviated filename to an exact path name */ - fullname = expand_dynamic_library_name(filename); - -+ /* SELinux checks db_database:{load_module} */ -+ sepgsql_database_load_module(MyDatabaseId, fullname); -+ - /* Unload the library if currently loaded */ - internal_unload_library(fullname); - -diff -Nrpc base/src/backend/utils/fmgr/fmgr.c sepgsql/src/backend/utils/fmgr/fmgr.c -*** base/src/backend/utils/fmgr/fmgr.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/backend/utils/fmgr/fmgr.c Tue Dec 15 17:30:25 2009 -*************** -*** 24,29 **** ---- 24,30 ---- - #include "miscadmin.h" - #include "nodes/nodeFuncs.h" - #include "pgstat.h" -+ #include "security/sepgsql.h" - #include "utils/builtins.h" - #include "utils/fmgrtab.h" - #include "utils/guc.h" -*************** fmgr_info_cxt_security(Oid functionId, F -*** 289,294 **** ---- 290,296 ---- - } - - finfo->fn_oid = functionId; -+ sepgsql_proc_entrypoint(finfo, procedureTuple); - ReleaseSysCache(procedureTuple); - } - -diff -Nrpc base/src/backend/utils/init/postinit.c sepgsql/src/backend/utils/init/postinit.c -*** base/src/backend/utils/init/postinit.c Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/utils/init/postinit.c Thu Sep 17 17:04:16 2009 -*************** -*** 32,37 **** ---- 32,38 ---- - #include "pgstat.h" - #include "postmaster/autovacuum.h" - #include "postmaster/postmaster.h" -+ #include "security/sepgsql.h" - #include "storage/backendid.h" - #include "storage/bufmgr.h" - #include "storage/fd.h" -*************** CheckMyDatabase(const char *name, bool a -*** 201,207 **** - name))); - - /* -! * Check privilege to connect to the database. (The am_superuser test - * is redundant, but since we have the flag, might as well check it - * and save a few cycles.) - */ ---- 202,208 ---- - name))); - - /* -! * Check privilege to connect to the database. (The am_superuser test - * is redundant, but since we have the flag, might as well check it - * and save a few cycles.) - */ -*************** CheckMyDatabase(const char *name, bool a -*** 213,218 **** ---- 214,222 ---- - errmsg("permission denied for database \"%s\"", name), - errdetail("User does not have CONNECT privilege."))); - -+ /* SELinux: db_database:{access} */ -+ sepgsql_database_access(MyDatabaseId); -+ - /* - * Check connection limit for this database. - * -*************** InitPostgres(const char *in_dbname, Oid -*** 575,580 **** ---- 579,587 ---- - */ - RelationCacheInitializePhase2(); - -+ /* Initialize SE-PostgreSQL internal facilities */ -+ sepgsqlAvcInitialize(); -+ - /* - * Figure out our postgres user id, and see if we are a superuser. - * -diff -Nrpc base/src/backend/utils/misc/guc.c sepgsql/src/backend/utils/misc/guc.c -*** base/src/backend/utils/misc/guc.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/backend/utils/misc/guc.c Tue Dec 15 17:30:25 2009 -*************** -*** 56,61 **** ---- 56,62 ---- - #include "postmaster/syslogger.h" - #include "postmaster/walwriter.h" - #include "regex/regex.h" -+ #include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/fd.h" - #include "tcop/tcopprot.h" -*************** static struct config_bool ConfigureNames -*** 1220,1225 **** ---- 1221,1252 ---- - &IgnoreSystemIndexes, - false, NULL, NULL - }, -+ #ifdef HAVE_SELINUX -+ { -+ {"sepostgresql", PGC_POSTMASTER, CONN_AUTH_SECURITY, -+ gettext_noop("SE-PostgreSQL activation option to be turned on/off"), -+ NULL, -+ }, -+ &sepostgresql_is_enabled, -+ false, NULL, NULL -+ }, -+ { -+ {"sepostgresql_row_level", PGC_POSTMASTER, CONN_AUTH_SECURITY, -+ gettext_noop("Row-level access controls on SE-PostgreSQL"), -+ NULL, -+ }, -+ &sepostgresql_row_level, -+ true, NULL, NULL -+ }, -+ { -+ {"sepostgresql_mcstrans", PGC_USERSET, CONN_AUTH_SECURITY, -+ gettext_noop("SE-PostgreSQL uses mcstrans on printing security labels"), -+ NULL, -+ }, -+ &sepostgresql_use_mcstrans, -+ true, NULL, NULL -+ }, -+ #endif - - /* End-of-list marker */ - { -diff -Nrpc base/src/backend/utils/misc/postgresql.conf.sample sepgsql/src/backend/utils/misc/postgresql.conf.sample -*** base/src/backend/utils/misc/postgresql.conf.sample Sun Sep 6 19:40:49 2009 ---- sepgsql/src/backend/utils/misc/postgresql.conf.sample Tue Dec 1 17:11:40 2009 -*************** -*** 51,57 **** - - - #------------------------------------------------------------------------------ -! # CONNECTIONS AND AUTHENTICATION - #------------------------------------------------------------------------------ - - # - Connection Settings - ---- 51,57 ---- - - - #------------------------------------------------------------------------------ -! # CONNECTIONS, AUTHENTICATION AND SECURITY - #------------------------------------------------------------------------------ - - # - Connection Settings - -*************** -*** 95,101 **** - # 0 selects the system default - #tcp_keepalives_count = 0 # TCP_KEEPCNT; - # 0 selects the system default -! - - #------------------------------------------------------------------------------ - # RESOURCE USAGE (except WAL) ---- 95,101 ---- - # 0 selects the system default - #tcp_keepalives_count = 0 # TCP_KEEPCNT; - # 0 selects the system default -! #sepostgresql = off # SE-PostgreSQL support - - #------------------------------------------------------------------------------ - # RESOURCE USAGE (except WAL) -diff -Nrpc base/src/backend/utils/misc/superuser.c sepgsql/src/backend/utils/misc/superuser.c -*** base/src/backend/utils/misc/superuser.c Sat Jan 3 13:01:35 2009 ---- sepgsql/src/backend/utils/misc/superuser.c Thu Sep 17 17:04:16 2009 -*************** -*** 21,26 **** ---- 21,27 ---- - #include "postgres.h" - - #include "catalog/pg_authid.h" -+ #include "security/sepgsql.h" - #include "utils/inval.h" - #include "utils/syscache.h" - #include "miscadmin.h" -*************** superuser_arg(Oid roleid) -*** 60,66 **** - - /* Quick out for cache hit */ - if (OidIsValid(last_roleid) && last_roleid == roleid) -! return last_roleid_is_super; - - /* Special escape path in case you deleted all your users. */ - if (!IsUnderPostmaster && roleid == BOOTSTRAP_SUPERUSERID) ---- 61,70 ---- - - /* Quick out for cache hit */ - if (OidIsValid(last_roleid) && last_roleid == roleid) -! { -! result = last_roleid_is_super; -! goto out; -! } - - /* Special escape path in case you deleted all your users. */ - if (!IsUnderPostmaster && roleid == BOOTSTRAP_SUPERUSERID) -*************** superuser_arg(Oid roleid) -*** 94,99 **** ---- 98,107 ---- - last_roleid = roleid; - last_roleid_is_super = result; - -+ out: -+ if (result) -+ result = sepgsql_database_superuser(MyDatabaseId); -+ - return result; - } - -diff -Nrpc base/src/bin/initdb/initdb.c sepgsql/src/bin/initdb/initdb.c -*** base/src/bin/initdb/initdb.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/bin/initdb/initdb.c Tue Dec 15 17:30:25 2009 -*************** static bool debug = false; -*** 87,92 **** ---- 87,93 ---- - static bool noclean = false; - static bool show_setting = false; - static char *xlog_dir = ""; -+ static bool enable_selinux = false; - - - /* internal vars */ -*************** setup_config(void) -*** 1205,1210 **** ---- 1206,1218 ---- - "#default_text_search_config = 'pg_catalog.simple'", - repltok); - -+ if (enable_selinux) -+ { -+ strcpy(repltok, "sepostgresql = on"); -+ conflines = replace_token(conflines, -+ "#sepostgresql = off", repltok); -+ } -+ - snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data); - - writefile(path, conflines); -*************** usage(const char *progname) -*** 2443,2448 **** ---- 2451,2457 ---- - printf(_(" -U, --username=NAME database superuser name\n")); - printf(_(" -W, --pwprompt prompt for a password for the new superuser\n")); - printf(_(" -X, --xlogdir=XLOGDIR location for the transaction log directory\n")); -+ printf(_(" --enable-selinux enables SELinux support, if compiled\n")); - printf(_("\nLess commonly used options:\n")); - printf(_(" -d, --debug generate lots of debugging output\n")); - printf(_(" -L DIRECTORY where to find the input files\n")); -*************** main(int argc, char *argv[]) -*** 2478,2483 **** ---- 2487,2493 ---- - {"auth", required_argument, NULL, 'A'}, - {"pwprompt", no_argument, NULL, 'W'}, - {"pwfile", required_argument, NULL, 9}, -+ {"enable-selinux", no_argument, NULL, 10}, - {"username", required_argument, NULL, 'U'}, - {"help", no_argument, NULL, '?'}, - {"version", no_argument, NULL, 'V'}, -*************** main(int argc, char *argv[]) -*** 2594,2599 **** ---- 2604,2612 ---- - case 9: - pwfilename = xstrdup(optarg); - break; -+ case 10: -+ enable_selinux = true; -+ break; - case 's': - show_setting = true; - break; -diff -Nrpc base/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c -*** base/src/bin/pg_dump/pg_dump.c Tue Dec 15 17:16:51 2009 ---- sepgsql/src/bin/pg_dump/pg_dump.c Tue Dec 15 17:30:25 2009 -*************** static int disable_dollar_quoting = 0; -*** 112,117 **** ---- 112,119 ---- - static int dump_inserts = 0; - static int column_inserts = 0; - -+ /* flag to turn on/off security_context */ -+ static int security_context = 0; - - static void help(const char *progname); - static void expand_schema_name_patterns(SimpleStringList *patterns, -*************** main(int argc, char **argv) -*** 277,282 **** ---- 279,285 ---- - {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, - {"role", required_argument, NULL, 3}, - {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, -+ {"security-context", no_argument, &security_context, 1}, - - {NULL, 0, NULL, 0} - }; -*************** main(int argc, char **argv) -*** 425,430 **** ---- 428,435 ---- - outputNoTablespaces = 1; - else if (strcmp(optarg, "use-set-session-authorization") == 0) - use_setsessauth = 1; -+ else if (strcmp(optarg, "security-context") == 0) -+ security_context = 1; - else - { - fprintf(stderr, -*************** main(int argc, char **argv) -*** 573,578 **** ---- 578,605 ---- - std_strings = PQparameterStatus(g_conn, "standard_conforming_strings"); - g_fout->std_strings = (std_strings && strcmp(std_strings, "on") == 0); - -+ /* Check availability of SE-PostgreSQL */ -+ if (security_context > 0) -+ { -+ PGresult *res; -+ -+ res = PQexec(g_conn, "SHOW sepostgresql"); -+ if (PQresultStatus(res) != PGRES_TUPLES_OK || -+ PQntuples(res) != 1 || -+ strcmp(PQgetvalue(res, 0, 0), "on") != 0) -+ { -+ write_msg(NULL, "SE-PostgreSQL is not available now."); -+ exit(1); -+ } -+ } -+ -+ /* -+ * It needs to force column insertion mode, when --inserts -+ * and either --security-label or --security-acl is given. -+ */ -+ if (security_context > 0 && dump_inserts) -+ column_inserts = 1; -+ - /* Set the role if requested */ - if (use_role && g_fout->remoteVersion >= 80100) - { -*************** help(const char *progname) -*** 826,831 **** ---- 853,860 ---- - printf(_(" --use-set-session-authorization\n" - " use SET SESSION AUTHORIZATION commands instead of\n" - " ALTER OWNER commands to set ownership\n")); -+ printf(_(" --security-label dump SE-PostgreSQL security labels\n")); -+ printf(_(" --security-acl dump row-level database ACLs\n")); - - printf(_("\nConnection options:\n")); - printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); -*************** dumpTableData_insert(Archive *fout, void -*** 1227,1233 **** - if (fout->remoteVersion >= 70100) - { - appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " -! "SELECT * FROM ONLY %s", - fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, - classname)); - } ---- 1256,1263 ---- - if (fout->remoteVersion >= 70100) - { - appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " -! "SELECT %s* FROM ONLY %s", -! (security_context > 0 ? "security_context, " : ""), - fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, - classname)); - } -*************** dumpDatabase(Archive *AH) -*** 1583,1589 **** - i_collate, - i_ctype, - i_frozenxid, -! i_tablespace; - CatalogId dbCatId; - DumpId dbDumpId; - const char *datname, ---- 1613,1620 ---- - i_collate, - i_ctype, - i_frozenxid, -! i_tablespace, -! i_seclabel; - CatalogId dbCatId; - DumpId dbDumpId; - const char *datname, -*************** dumpDatabase(Archive *AH) -*** 1591,1597 **** - *encoding, - *collate, - *ctype, -! *tablespace; - uint32 frozenxid; - - datname = PQdb(g_conn); ---- 1622,1629 ---- - *encoding, - *collate, - *ctype, -! *tablespace, -! *seclabel; - uint32 frozenxid; - - datname = PQdb(g_conn); -*************** dumpDatabase(Archive *AH) -*** 1610,1620 **** - "pg_encoding_to_char(encoding) AS encoding, " - "datcollate, datctype, datfrozenxid, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " -! "shobj_description(oid, 'pg_database') AS description " -! - "FROM pg_database " - "WHERE datname = ", -! username_subquery); - appendStringLiteralAH(dbQry, datname, AH); - } - else if (g_fout->remoteVersion >= 80200) ---- 1642,1653 ---- - "pg_encoding_to_char(encoding) AS encoding, " - "datcollate, datctype, datfrozenxid, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " -! "shobj_description(oid, 'pg_database') AS description, " -! "%s as security_context " - "FROM pg_database " - "WHERE datname = ", -! username_subquery, -! security_context ? "security_context" : "NULL"); - appendStringLiteralAH(dbQry, datname, AH); - } - else if (g_fout->remoteVersion >= 80200) -*************** dumpDatabase(Archive *AH) -*** 1624,1631 **** - "pg_encoding_to_char(encoding) AS encoding, " - "NULL AS datcollate, NULL AS datctype, datfrozenxid, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " -! "shobj_description(oid, 'pg_database') AS description " -! - "FROM pg_database " - "WHERE datname = ", - username_subquery); ---- 1657,1664 ---- - "pg_encoding_to_char(encoding) AS encoding, " - "NULL AS datcollate, NULL AS datctype, datfrozenxid, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " -! "shobj_description(oid, 'pg_database') AS description, " -! "NULL as security_context " - "FROM pg_database " - "WHERE datname = ", - username_subquery); -*************** dumpDatabase(Archive *AH) -*** 1637,1643 **** - "(%s datdba) AS dba, " - "pg_encoding_to_char(encoding) AS encoding, " - "NULL AS datcollate, NULL AS datctype, datfrozenxid, " -! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace " - "FROM pg_database " - "WHERE datname = ", - username_subquery); ---- 1670,1677 ---- - "(%s datdba) AS dba, " - "pg_encoding_to_char(encoding) AS encoding, " - "NULL AS datcollate, NULL AS datctype, datfrozenxid, " -! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " -! "NULL as security_context " - "FROM pg_database " - "WHERE datname = ", - username_subquery); -*************** dumpDatabase(Archive *AH) -*** 1650,1656 **** - "pg_encoding_to_char(encoding) AS encoding, " - "NULL AS datcollate, NULL AS datctype, " - "0 AS datfrozenxid, " -! "NULL AS tablespace " - "FROM pg_database " - "WHERE datname = ", - username_subquery); ---- 1684,1691 ---- - "pg_encoding_to_char(encoding) AS encoding, " - "NULL AS datcollate, NULL AS datctype, " - "0 AS datfrozenxid, " -! "NULL AS tablespace, " -! "NULL AS security_context " - "FROM pg_database " - "WHERE datname = ", - username_subquery); -*************** dumpDatabase(Archive *AH) -*** 1665,1671 **** - "pg_encoding_to_char(encoding) AS encoding, " - "NULL AS datcollate, NULL AS datctype, " - "0 AS datfrozenxid, " -! "NULL AS tablespace " - "FROM pg_database " - "WHERE datname = ", - username_subquery); ---- 1700,1707 ---- - "pg_encoding_to_char(encoding) AS encoding, " - "NULL AS datcollate, NULL AS datctype, " - "0 AS datfrozenxid, " -! "NULL AS tablespace, " -! "NULL as security_context " - "FROM pg_database " - "WHERE datname = ", - username_subquery); -*************** dumpDatabase(Archive *AH) -*** 1699,1704 **** ---- 1735,1741 ---- - i_ctype = PQfnumber(res, "datctype"); - i_frozenxid = PQfnumber(res, "datfrozenxid"); - i_tablespace = PQfnumber(res, "tablespace"); -+ i_seclabel = PQfnumber(res, "security_context"); - - dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid)); - dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid)); -*************** dumpDatabase(Archive *AH) -*** 1708,1713 **** ---- 1745,1751 ---- - ctype = PQgetvalue(res, 0, i_ctype); - frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid)); - tablespace = PQgetvalue(res, 0, i_tablespace); -+ seclabel = PQgetvalue(res, 0, i_seclabel); - - appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0", - fmtId(datname)); -*************** dumpDatabase(Archive *AH) -*** 1729,1734 **** ---- 1767,1775 ---- - if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0) - appendPQExpBuffer(creaQry, " TABLESPACE = %s", - fmtId(tablespace)); -+ if (strlen(seclabel) > 0) -+ appendPQExpBuffer(creaQry, " SECURITY_CONTEXT = '%s'", seclabel); -+ - appendPQExpBuffer(creaQry, ";\n"); - - if (binary_upgrade) -*************** getTables(int *numTables) -*** 3190,3195 **** ---- 3231,3237 ---- - int i_reltablespace; - int i_reloptions; - int i_toastreloptions; -+ int i_relseclabel; - - /* Make sure we are in proper schema */ - selectSourceSchema("pg_catalog"); -*************** getTables(int *numTables) -*** 3231,3237 **** - "d.refobjsubid AS owning_col, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "array_to_string(c.reloptions, ', ') AS reloptions, " -! "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " ---- 3273,3280 ---- - "d.refobjsubid AS owning_col, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "array_to_string(c.reloptions, ', ') AS reloptions, " -! "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, " -! "%s as security_context " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -*************** getTables(int *numTables) -*** 3242,3247 **** ---- 3285,3291 ---- - "WHERE c.relkind in ('%c', '%c', '%c', '%c') " - "ORDER BY c.oid", - username_subquery, -+ security_context ? "c.security_context" : "NULL", - RELKIND_SEQUENCE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); -*************** getTables(int *numTables) -*** 3263,3269 **** - "d.refobjsubid AS owning_col, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "array_to_string(c.reloptions, ', ') AS reloptions, " -! "NULL AS toast_reloptions " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " ---- 3307,3314 ---- - "d.refobjsubid AS owning_col, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "array_to_string(c.reloptions, ', ') AS reloptions, " -! "NULL AS toast_reloptions, " -! "NULL as security_context " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -*************** getTables(int *numTables) -*** 3294,3300 **** - "d.refobjsubid AS owning_col, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "NULL AS reloptions, " -! "NULL AS toast_reloptions " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " ---- 3339,3346 ---- - "d.refobjsubid AS owning_col, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "NULL AS reloptions, " -! "NULL AS toast_reloptions, " -! "NULL as security_context " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -*************** getTables(int *numTables) -*** 3325,3331 **** - "d.refobjsubid AS owning_col, " - "NULL AS reltablespace, " - "NULL AS reloptions, " -! "NULL AS toast_reloptions " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " ---- 3371,3378 ---- - "d.refobjsubid AS owning_col, " - "NULL AS reltablespace, " - "NULL AS reloptions, " -! "NULL AS toast_reloptions, " -! "NULL as security_context " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -*************** getTables(int *numTables) -*** 3352,3358 **** - "NULL::int4 AS owning_col, " - "NULL AS reltablespace, " - "NULL AS reloptions, " -! "NULL AS toast_reloptions " - "FROM pg_class " - "WHERE relkind IN ('%c', '%c', '%c') " - "ORDER BY oid", ---- 3399,3406 ---- - "NULL::int4 AS owning_col, " - "NULL AS reltablespace, " - "NULL AS reloptions, " -! "NULL AS toast_reloptions, " -! "NULL AS security_context " - "FROM pg_class " - "WHERE relkind IN ('%c', '%c', '%c') " - "ORDER BY oid", -*************** getTables(int *numTables) -*** 3374,3380 **** - "NULL::int4 AS owning_col, " - "NULL AS reltablespace, " - "NULL AS reloptions, " -! "NULL AS toast_reloptions " - "FROM pg_class " - "WHERE relkind IN ('%c', '%c', '%c') " - "ORDER BY oid", ---- 3422,3429 ---- - "NULL::int4 AS owning_col, " - "NULL AS reltablespace, " - "NULL AS reloptions, " -! "NULL AS toast_reloptions, " -! "NULL AS security_context " - "FROM pg_class " - "WHERE relkind IN ('%c', '%c', '%c') " - "ORDER BY oid", -*************** getTables(int *numTables) -*** 3406,3412 **** - "NULL::int4 AS owning_col, " - "NULL AS reltablespace, " - "NULL AS reloptions, " -! "NULL AS toast_reloptions " - "FROM pg_class c " - "WHERE relkind IN ('%c', '%c') " - "ORDER BY oid", ---- 3455,3462 ---- - "NULL::int4 AS owning_col, " - "NULL AS reltablespace, " - "NULL AS reloptions, " -! "NULL AS toast_reloptions, " -! "NULL as security_context " - "FROM pg_class c " - "WHERE relkind IN ('%c', '%c') " - "ORDER BY oid", -*************** getTables(int *numTables) -*** 3451,3456 **** ---- 3501,3507 ---- - i_reltablespace = PQfnumber(res, "reltablespace"); - i_reloptions = PQfnumber(res, "reloptions"); - i_toastreloptions = PQfnumber(res, "toast_reloptions"); -+ i_relseclabel = PQfnumber(res, "security_context"); - - if (lockWaitTimeout && g_fout->remoteVersion >= 70300) - { -*************** getTables(int *numTables) -*** 3498,3503 **** ---- 3549,3555 ---- - tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace)); - tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions)); - tblinfo[i].toast_reloptions = strdup(PQgetvalue(res, i, i_toastreloptions)); -+ tblinfo[i].relseclabel = strdup(PQgetvalue(res, i, i_relseclabel)); - - /* other fields were zeroed above */ - -*************** getTableAttrs(TableInfo *tblinfo, int nu -*** 4697,4702 **** ---- 4749,4755 ---- - int i_attlen; - int i_attalign; - int i_attislocal; -+ int i_attseclabel; - PGresult *res; - int ntups; - bool hasdefaults; -*************** getTableAttrs(TableInfo *tblinfo, int nu -*** 4741,4752 **** - "a.attstattarget, a.attstorage, t.typstorage, " - "a.attnotnull, a.atthasdef, a.attisdropped, " - "a.attlen, a.attalign, a.attislocal, " -! "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname " - "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " - "ON a.atttypid = t.oid " - "WHERE a.attrelid = '%u'::pg_catalog.oid " - "AND a.attnum > 0::pg_catalog.int2 " - "ORDER BY a.attrelid, a.attnum", - tbinfo->dobj.catId.oid); - } - else if (g_fout->remoteVersion >= 70100) ---- 4794,4807 ---- - "a.attstattarget, a.attstorage, t.typstorage, " - "a.attnotnull, a.atthasdef, a.attisdropped, " - "a.attlen, a.attalign, a.attislocal, " -! "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " -! "%s as security_context " - "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " - "ON a.atttypid = t.oid " - "WHERE a.attrelid = '%u'::pg_catalog.oid " - "AND a.attnum > 0::pg_catalog.int2 " - "ORDER BY a.attrelid, a.attnum", -+ security_context ? "a.security_context" : "NULL", - tbinfo->dobj.catId.oid); - } - else if (g_fout->remoteVersion >= 70100) -*************** getTableAttrs(TableInfo *tblinfo, int nu -*** 4761,4767 **** - "t.typstorage, a.attnotnull, a.atthasdef, " - "false AS attisdropped, a.attlen, " - "a.attalign, false AS attislocal, " -! "format_type(t.oid,a.atttypmod) AS atttypname " - "FROM pg_attribute a LEFT JOIN pg_type t " - "ON a.atttypid = t.oid " - "WHERE a.attrelid = '%u'::oid " ---- 4816,4823 ---- - "t.typstorage, a.attnotnull, a.atthasdef, " - "false AS attisdropped, a.attlen, " - "a.attalign, false AS attislocal, " -! "format_type(t.oid,a.atttypmod) AS atttypname, " -! "NULL as security_context " - "FROM pg_attribute a LEFT JOIN pg_type t " - "ON a.atttypid = t.oid " - "WHERE a.attrelid = '%u'::oid " -*************** getTableAttrs(TableInfo *tblinfo, int nu -*** 4778,4784 **** - "attnotnull, atthasdef, false AS attisdropped, " - "attlen, attalign, " - "false AS attislocal, " -! "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname " - "FROM pg_attribute a " - "WHERE attrelid = '%u'::oid " - "AND attnum > 0::int2 " ---- 4834,4841 ---- - "attnotnull, atthasdef, false AS attisdropped, " - "attlen, attalign, " - "false AS attislocal, " -! "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, " -! "NULL as security_context " - "FROM pg_attribute a " - "WHERE attrelid = '%u'::oid " - "AND attnum > 0::int2 " -*************** getTableAttrs(TableInfo *tblinfo, int nu -*** 4804,4809 **** ---- 4861,4867 ---- - i_attlen = PQfnumber(res, "attlen"); - i_attalign = PQfnumber(res, "attalign"); - i_attislocal = PQfnumber(res, "attislocal"); -+ i_attseclabel = PQfnumber(res, "security_context"); - - tbinfo->numatts = ntups; - tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); -*************** getTableAttrs(TableInfo *tblinfo, int nu -*** 4816,4821 **** ---- 4874,4880 ---- - tbinfo->attlen = (int *) malloc(ntups * sizeof(int)); - tbinfo->attalign = (char *) malloc(ntups * sizeof(char)); - tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool)); -+ tbinfo->attseclabel = (char **) malloc(ntups * sizeof(char *)); - tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool)); - tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *)); - tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); -*************** getTableAttrs(TableInfo *tblinfo, int nu -*** 4841,4846 **** ---- 4900,4906 ---- - tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen)); - tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign)); - tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't'); -+ tbinfo->attseclabel[j] = strdup(PQgetvalue(res, j, i_attseclabel)); - tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't'); - tbinfo->attrdefs[j] = NULL; /* fix below */ - if (PQgetvalue(res, j, i_atthasdef)[0] == 't') -*************** dumpFunc(Archive *fout, FuncInfo *finfo) -*** 7091,7096 **** ---- 7151,7157 ---- - char *proconfig; - char *procost; - char *prorows; -+ char *proseclabel; - char *lanname; - char *rettypename; - int nallargs; -*************** dumpFunc(Archive *fout, FuncInfo *finfo) -*** 7127,7135 **** - "pg_catalog.pg_get_function_result(oid) AS funcresult, " - "proiswindow, provolatile, proisstrict, prosecdef, " - "proconfig, procost, prorows, " -! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); - } - else if (g_fout->remoteVersion >= 80300) ---- 7188,7198 ---- - "pg_catalog.pg_get_function_result(oid) AS funcresult, " - "proiswindow, provolatile, proisstrict, prosecdef, " - "proconfig, procost, prorows, " -! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " -! "%s as security_context " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", -+ security_context ? "security_context" : "NULL", - finfo->dobj.catId.oid); - } - else if (g_fout->remoteVersion >= 80300) -*************** dumpFunc(Archive *fout, FuncInfo *finfo) -*** 7140,7146 **** - "false AS proiswindow, " - "provolatile, proisstrict, prosecdef, " - "proconfig, procost, prorows, " -! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); ---- 7203,7210 ---- - "false AS proiswindow, " - "provolatile, proisstrict, prosecdef, " - "proconfig, procost, prorows, " -! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " -! "NULL AS security_context " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); -*************** dumpFunc(Archive *fout, FuncInfo *finfo) -*** 7153,7159 **** - "false AS proiswindow, " - "provolatile, proisstrict, prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " -! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); ---- 7217,7224 ---- - "false AS proiswindow, " - "provolatile, proisstrict, prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " -! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " -! "NULL AS security_context " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); -*************** dumpFunc(Archive *fout, FuncInfo *finfo) -*** 7168,7174 **** - "false AS proiswindow, " - "provolatile, proisstrict, prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " -! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); ---- 7233,7240 ---- - "false AS proiswindow, " - "provolatile, proisstrict, prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " -! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " -! "NULL AS security_context " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); -*************** dumpFunc(Archive *fout, FuncInfo *finfo) -*** 7183,7189 **** - "false AS proiswindow, " - "provolatile, proisstrict, prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " -! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); ---- 7249,7256 ---- - "false AS proiswindow, " - "provolatile, proisstrict, prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " -! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " -! "NULL AS security_context " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); -*************** dumpFunc(Archive *fout, FuncInfo *finfo) -*** 7200,7206 **** - "proisstrict, " - "false AS prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " -! "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " - "FROM pg_proc " - "WHERE oid = '%u'::oid", - finfo->dobj.catId.oid); ---- 7267,7274 ---- - "proisstrict, " - "false AS prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " -! "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname, " -! "NULL AS security_context " - "FROM pg_proc " - "WHERE oid = '%u'::oid", - finfo->dobj.catId.oid); -*************** dumpFunc(Archive *fout, FuncInfo *finfo) -*** 7217,7223 **** - "false AS proisstrict, " - "false AS prosecdef, " - "NULL AS proconfig, 0 AS procost, 0 AS prorows, " -! "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " - "FROM pg_proc " - "WHERE oid = '%u'::oid", - finfo->dobj.catId.oid); ---- 7285,7292 ---- - "false AS proisstrict, " - "false AS prosecdef, " - "NULL AS proconfig, 0 AS procost, 0 AS prorows, " -! "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname, " -! "NULL AS security_context " - "FROM pg_proc " - "WHERE oid = '%u'::oid", - finfo->dobj.catId.oid); -*************** dumpFunc(Archive *fout, FuncInfo *finfo) -*** 7261,7266 **** ---- 7330,7336 ---- - proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig")); - procost = PQgetvalue(res, 0, PQfnumber(res, "procost")); - prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); -+ proseclabel = PQgetvalue(res, 0, PQfnumber(res, "security_context")); - lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); - - /* -*************** dumpFunc(Archive *fout, FuncInfo *finfo) -*** 7419,7424 **** ---- 7489,7497 ---- - if (prosecdef[0] == 't') - appendPQExpBuffer(q, " SECURITY DEFINER"); - -+ if (security_context > 0 && strlen(proseclabel) > 0) -+ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", proseclabel); -+ - /* - * COST and ROWS are emitted only if present and not default, so as not to - * break backwards-compatibility of the dump without need. Keep this code -*************** dumpTableSchema(Archive *fout, TableInfo -*** 9877,9882 **** ---- 9950,9966 ---- - if (tbinfo->notnull[j] && - (!tbinfo->inhNotNull[j] || binary_upgrade)) - appendPQExpBuffer(q, " NOT NULL"); -+ -+ /* -+ * Security label -- if SE-PostgreSQL enabled -+ */ -+ if (security_context > 0 && -+ strlen(tbinfo->attseclabel[j]) > 0 && -+ strcmp(tbinfo->relseclabel, tbinfo->attseclabel[j]) != 0) -+ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", -+ tbinfo->attseclabel[j]); -+ -+ actual_atts++; - } - } - -*************** dumpTableSchema(Archive *fout, TableInfo -*** 9939,9944 **** ---- 10023,10031 ---- - appendPQExpBuffer(q, ")"); - } - -+ if (security_context > 0 && strlen(tbinfo->relseclabel) > 0) -+ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", tbinfo->relseclabel); -+ - appendPQExpBuffer(q, ";\n"); - - /* -*************** fmtCopyColumnList(const TableInfo *ti) -*** 11510,11515 **** ---- 11597,11609 ---- - - appendPQExpBuffer(q, "("); - needComma = false; -+ -+ if (security_context > 0) -+ { -+ appendPQExpBuffer(q, "security_context"); -+ needComma = true; -+ } -+ - for (i = 0; i < numatts; i++) - { - if (attisdropped[i]) -diff -Nrpc base/src/bin/pg_dump/pg_dump.h sepgsql/src/bin/pg_dump/pg_dump.h -*** base/src/bin/pg_dump/pg_dump.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/bin/pg_dump/pg_dump.h Wed Jul 15 20:03:59 2009 -*************** typedef struct _tableInfo -*** 228,233 **** ---- 228,234 ---- - bool hasoids; /* does it have OIDs? */ - uint32 frozenxid; /* for restore frozen xid */ - int ncheck; /* # of CHECK expressions */ -+ char *relseclabel; /* security labels of relation */ - /* these two are set only if table is a sequence owned by a column: */ - Oid owning_tab; /* OID of table owning sequence */ - int owning_col; /* attr # of column owning sequence */ -*************** typedef struct _tableInfo -*** 249,254 **** ---- 250,256 ---- - int *attlen; /* attribute length, used by binary_upgrade */ - char *attalign; /* attribute align, used by binary_upgrade */ - bool *attislocal; /* true if attr has local definition */ -+ char **attseclabel; /* security labels of attributes */ - - /* - * Note: we need to store per-attribute notnull, default, and constraint -diff -Nrpc base/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpall.c -*** base/src/bin/pg_dump/pg_dumpall.c Thu Jun 18 10:20:52 2009 ---- sepgsql/src/bin/pg_dump/pg_dumpall.c Wed Jul 15 20:03:59 2009 -*************** static int no_tablespaces = 0; -*** 69,74 **** ---- 69,77 ---- - static int use_setsessauth = 0; - static int server_version; - -+ static int security_label = 0; -+ static int security_acl = 0; -+ - static FILE *OPF; - static char *filename = NULL; - -*************** main(int argc, char *argv[]) -*** 130,135 **** ---- 133,140 ---- - {"no-tablespaces", no_argument, &no_tablespaces, 1}, - {"role", required_argument, NULL, 3}, - {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, -+ {"security-label", no_argument, &security_label, 1}, -+ {"security-acl", no_argument, &security_acl, 1}, - - {NULL, 0, NULL, 0} - }; -*************** main(int argc, char *argv[]) -*** 283,288 **** ---- 288,297 ---- - no_tablespaces = 1; - else if (strcmp(optarg, "use-set-session-authorization") == 0) - use_setsessauth = 1; -+ else if (strcmp(optarg, "security-label") == 0) -+ security_label = 1; -+ else if (strcmp(optarg, "security-acl") == 0) -+ security_acl = 1; - else - { - fprintf(stderr, -*************** main(int argc, char *argv[]) -*** 328,333 **** ---- 337,346 ---- - appendPQExpBuffer(pgdumpopts, " --no-tablespaces"); - if (use_setsessauth) - appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); -+ if (security_label) -+ appendPQExpBuffer(pgdumpopts, " --security-label"); -+ if (security_acl) -+ appendPQExpBuffer(pgdumpopts, " --security-acl"); - - if (optind < argc) - { -*************** main(int argc, char *argv[]) -*** 403,408 **** ---- 416,434 ---- - } - } - -+ if (security_label > 0) -+ { -+ PGresult *res -+ = PQexec(conn, "SHOW sepostgresql"); -+ if (PQresultStatus(res) != PGRES_TUPLES_OK || -+ PQntuples(res) != 1 || -+ strcmp(PQgetvalue(res, 0, 0), "on") != 0) -+ { -+ fprintf(stderr, "SE-PostgreSQL is not available now."); -+ exit(1); -+ } -+ } -+ - /* - * Open the output file if required, otherwise use stdout - */ -*************** dumpCreateDB(PGconn *conn) -*** 1130,1184 **** - - /* Now collect all the information about databases to dump */ - if (server_version >= 80400) -! res = executeQuery(conn, -! "SELECT datname, " - "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "datcollate, datctype, datfrozenxid, " - "datistemplate, datacl, datconnlimit, " -! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " - "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " -! "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 80100) -! res = executeQuery(conn, -! "SELECT datname, " - "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, datfrozenxid, " - "datistemplate, datacl, datconnlimit, " -! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " - "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 80000) -! res = executeQuery(conn, -! "SELECT datname, " - "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, datfrozenxid, " - "datistemplate, datacl, -1 as datconnlimit, " -! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " - "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 70300) -! res = executeQuery(conn, -! "SELECT datname, " - "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, datfrozenxid, " - "datistemplate, datacl, -1 as datconnlimit, " -! "'pg_default' AS dattablespace " - "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 70100) -! res = executeQuery(conn, -! "SELECT datname, " - "coalesce(" - "(select usename from pg_shadow where usesysid=datdba), " - "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " - "datistemplate, '' as datacl, -1 as datconnlimit, " -! "'pg_default' AS dattablespace " - "FROM pg_database d " - "WHERE datallowconn ORDER BY 1"); - else ---- 1156,1211 ---- - - /* Now collect all the information about databases to dump */ - if (server_version >= 80400) -! appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "datcollate, datctype, datfrozenxid, " - "datistemplate, datacl, datconnlimit, " -! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " -! "%s AS security_label " - "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " -! "WHERE datallowconn ORDER BY 1", -! security_label ? "sepgsql_raw_to_trans(datselabel)" : "null::text"); - else if (server_version >= 80100) -! appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, datfrozenxid, " - "datistemplate, datacl, datconnlimit, " -! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " -! "null::text " - "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 80000) -! appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, datfrozenxid, " - "datistemplate, datacl, -1 as datconnlimit, " -! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " -! "null::text " - "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 70300) -! appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, datfrozenxid, " - "datistemplate, datacl, -1 as datconnlimit, " -! "'pg_default' AS dattablespace, " -! "null::text " - "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 70100) -! appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(" - "(select usename from pg_shadow where usesysid=datdba), " - "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " - "datistemplate, '' as datacl, -1 as datconnlimit, " -! "'pg_default' AS dattablespace, " -! "null::text " - "FROM pg_database d " - "WHERE datallowconn ORDER BY 1"); - else -*************** dumpCreateDB(PGconn *conn) -*** 1187,1204 **** - * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal - * with getting a NULL by not printing any OWNER clause. - */ -! res = executeQuery(conn, -! "SELECT datname, " - "(select usename from pg_shadow where usesysid=datdba), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " - "'f' as datistemplate, " - "'' as datacl, -1 as datconnlimit, " -! "'pg_default' AS dattablespace " - "FROM pg_database d " - "ORDER BY 1"); - } - - for (i = 0; i < PQntuples(res); i++) - { - char *dbname = PQgetvalue(res, i, 0); ---- 1214,1233 ---- - * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal - * with getting a NULL by not printing any OWNER clause. - */ -! appendPQExpBuffer(buf, "SELECT datname, " - "(select usename from pg_shadow where usesysid=datdba), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " - "'f' as datistemplate, " - "'' as datacl, -1 as datconnlimit, " -! "'pg_default' AS dattablespace, " -! "null::text " - "FROM pg_database d " - "ORDER BY 1"); - } - -+ res = PQexec(conn, buf->data); -+ - for (i = 0; i < PQntuples(res); i++) - { - char *dbname = PQgetvalue(res, i, 0); -*************** dumpCreateDB(PGconn *conn) -*** 1211,1216 **** ---- 1240,1246 ---- - char *dbacl = PQgetvalue(res, i, 7); - char *dbconnlimit = PQgetvalue(res, i, 8); - char *dbtablespace = PQgetvalue(res, i, 9); -+ char *dbseclabel = PQgetvalue(res, i, 9); - char *fdbname; - - fdbname = strdup(fmtId(dbname)); -*************** dumpCreateDB(PGconn *conn) -*** 1266,1271 **** ---- 1296,1305 ---- - appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", - dbconnlimit); - -+ if (security_label > 0 && strlen(dbseclabel) > 0) -+ appendPQExpBuffer(buf, " SECURITY_LABEL = '%s'", -+ dbseclabel); -+ - appendPQExpBuffer(buf, ";\n"); - - if (strcmp(dbistemplate, "t") == 0) -diff -Nrpc base/src/include/access/htup.h sepgsql/src/include/access/htup.h -*** base/src/include/access/htup.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/access/htup.h Tue Sep 8 23:55:48 2009 -*************** typedef HeapTupleHeaderData *HeapTupleHe -*** 163,169 **** - #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ - #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ - #define HEAP_HASOID 0x0008 /* has an object-id field */ -! /* bit 0x0010 is available */ - #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ - #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ - #define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */ ---- 163,169 ---- - #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ - #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ - #define HEAP_HASOID 0x0008 /* has an object-id field */ -! #define HEAP_HASSECID 0x0010 /* has an security-id field */ - #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ - #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ - #define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */ -*************** do { \ -*** 290,295 **** ---- 290,298 ---- - (tup)->t_choice.t_datum.datum_typmod = (typmod) \ - ) - -+ #define HeapTupleHeaderHasOid(tup) \ -+ ((tup)->t_infomask & HEAP_HASOID) -+ - #define HeapTupleHeaderGetOid(tup) \ - ( \ - ((tup)->t_infomask & HEAP_HASOID) ? \ -*************** do { \ -*** 349,354 **** ---- 352,376 ---- - (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ - ) - -+ #define HeapTupleHeaderHasSecid(tup) \ -+ ((tup)->t_infomask & HEAP_HASSECID) -+ -+ #define HeapTupleHeaderGetSecid(tup) \ -+ ( \ -+ HeapTupleHeaderHasSecid(tup) \ -+ ? (*(Oid *)((char *)(tup) + (tup)->t_hoff \ -+ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ -+ - sizeof(Oid))) \ -+ : InvalidOid \ -+ ) -+ -+ #define HeapTupleHeaderSetSecid(tup, secid) \ -+ do { \ -+ Assert(HeapTupleHeaderHasSecid(tup)); \ -+ *((Oid *)((char *)(tup) + (tup)->t_hoff \ -+ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ -+ - sizeof(Oid))) = (secid); \ -+ } while(0) - - /* - * BITMAPLEN(NATTS) - -*************** typedef HeapTupleData *HeapTuple; -*** 549,554 **** ---- 571,584 ---- - #define HeapTupleSetOid(tuple, oid) \ - HeapTupleHeaderSetOid((tuple)->t_data, (oid)) - -+ #define HeapTupleHasSecid(tuple) \ -+ HeapTupleHeaderHasSecid((tuple)->t_data) -+ -+ #define HeapTupleGetSecid(tuple) \ -+ HeapTupleHeaderGetSecid((tuple)->t_data) -+ -+ #define HeapTupleSetSecid(tuple, secid) \ -+ HeapTupleHeaderSetSecid((tuple)->t_data, (secid)) - - /* - * WAL record definitions for heapam.c's WAL operations -diff -Nrpc base/src/include/access/sysattr.h sepgsql/src/include/access/sysattr.h -*** base/src/include/access/sysattr.h Sat Jan 3 12:25:21 2009 ---- sepgsql/src/include/access/sysattr.h Wed Sep 9 16:47:01 2009 -*************** -*** 25,31 **** - #define MaxTransactionIdAttributeNumber (-5) - #define MaxCommandIdAttributeNumber (-6) - #define TableOidAttributeNumber (-7) -! #define FirstLowInvalidHeapAttributeNumber (-8) - - - #endif /* SYSATTR_H */ ---- 25,43 ---- - #define MaxTransactionIdAttributeNumber (-5) - #define MaxCommandIdAttributeNumber (-6) - #define TableOidAttributeNumber (-7) -! #define SecurityAttributeNumber (-8) -! #define FirstLowInvalidHeapAttributeNumber (-9) - -+ /* -+ * Attribute names for the system-defined attributes -+ */ -+ #define SelfItemPointerAttributeName "ctid" -+ #define ObjectIdAttributeName "oid" -+ #define MinTransactionIdAttributeName "xmin" -+ #define MinCommandIdAttributeName "cmin" -+ #define MaxTransactionIdAttributeName "xmax" -+ #define MaxCommandIdAttributeName "cmax" -+ #define TableOidAttributeName "tableoid" -+ #define SecurityAttributeName "security_context" - - #endif /* SYSATTR_H */ -diff -Nrpc base/src/include/access/tupdesc.h sepgsql/src/include/access/tupdesc.h -*** base/src/include/access/tupdesc.h Sat Jan 3 12:25:21 2009 ---- sepgsql/src/include/access/tupdesc.h Wed Sep 9 13:14:37 2009 -*************** typedef struct tupleDesc -*** 75,80 **** ---- 75,81 ---- - Oid tdtypeid; /* composite type ID for tuple type */ - int32 tdtypmod; /* typmod for tuple type */ - bool tdhasoid; /* tuple has oid attribute in its header */ -+ bool tdhassecid; /* tuple has secid attribute in its header */ - int tdrefcount; /* reference count, or -1 if not counting */ - } *TupleDesc; - -diff -Nrpc base/src/include/bootstrap/bootstrap.h sepgsql/src/include/bootstrap/bootstrap.h -*** base/src/include/bootstrap/bootstrap.h Sat Jan 3 12:25:21 2009 ---- sepgsql/src/include/bootstrap/bootstrap.h Tue Dec 8 14:04:25 2009 -*************** typedef enum -*** 70,76 **** - BootstrapProcess, - StartupProcess, - BgWriterProcess, -! WalWriterProcess - } AuxProcType; - - #endif /* BOOTSTRAP_H */ ---- 70,77 ---- - BootstrapProcess, - StartupProcess, - BgWriterProcess, -! WalWriterProcess, -! SelinuxReceiverProcess, - } AuxProcType; - - #endif /* BOOTSTRAP_H */ -diff -Nrpc base/src/include/catalog/dependency.h sepgsql/src/include/catalog/dependency.h -*** base/src/include/catalog/dependency.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/catalog/dependency.h Thu Jul 16 17:22:29 2009 -*************** typedef enum ObjectClass -*** 155,160 **** ---- 155,163 ---- - extern void performDeletion(const ObjectAddress *object, - DropBehavior behavior); - -+ extern void performDeletionNoPerms(const ObjectAddress *object, -+ DropBehavior behavior); -+ - extern void performMultipleDeletions(const ObjectAddresses *objects, - DropBehavior behavior); - -diff -Nrpc base/src/include/catalog/heap.h sepgsql/src/include/catalog/heap.h -*** base/src/include/catalog/heap.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/catalog/heap.h Wed Jul 15 19:38:52 2009 -*************** extern Oid heap_create_with_catalog(cons -*** 56,62 **** - int oidinhcount, - OnCommitAction oncommit, - Datum reloptions, -! bool allow_system_table_mods); - - extern void heap_drop_with_catalog(Oid relid); - ---- 56,63 ---- - int oidinhcount, - OnCommitAction oncommit, - Datum reloptions, -! bool allow_system_table_mods, -! Oid *secLabels); - - extern void heap_drop_with_catalog(Oid relid); - -*************** extern List *heap_truncate_find_FKs(List -*** 68,79 **** - - extern void InsertPgAttributeTuple(Relation pg_attribute_rel, - Form_pg_attribute new_attribute, -! CatalogIndexState indstate); - - extern void InsertPgClassTuple(Relation pg_class_desc, - Relation new_rel_desc, - Oid new_rel_oid, -! Datum reloptions); - - extern List *AddRelationNewConstraints(Relation rel, - List *newColDefaults, ---- 69,82 ---- - - extern void InsertPgAttributeTuple(Relation pg_attribute_rel, - Form_pg_attribute new_attribute, -! CatalogIndexState indstate, -! Oid new_att_secid); - - extern void InsertPgClassTuple(Relation pg_class_desc, - Relation new_rel_desc, - Oid new_rel_oid, -! Datum reloptions, -! Oid new_rel_secid); - - extern List *AddRelationNewConstraints(Relation rel, - List *newColDefaults, -*************** extern Form_pg_attribute SystemAttribute -*** 103,108 **** ---- 106,113 ---- - extern Form_pg_attribute SystemAttributeByName(const char *attname, - bool relhasoids); - -+ extern bool SystemAttributeIsWritable(AttrNumber attnum); -+ - extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind); - - extern void CheckAttributeType(const char *attname, Oid atttypid); -diff -Nrpc base/src/include/catalog/indexing.h sepgsql/src/include/catalog/indexing.h -*** base/src/include/catalog/indexing.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/catalog/indexing.h Wed Jul 15 19:30:50 2009 -*************** DECLARE_UNIQUE_INDEX(pg_type_oid_index, -*** 249,254 **** ---- 249,259 ---- - DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); - #define TypeNameNspIndexId 2704 - -+ DECLARE_UNIQUE_INDEX(pg_security_secid_index, 3401, on pg_security using btree(secid oid_ops, datid oid_ops, relid oid_ops)); -+ #define SecuritySecidIndexId 3401 -+ DECLARE_UNIQUE_INDEX(pg_security_secattr_index, 3402, on pg_security using btree(datid oid_ops, relid oid_ops, seckind char_ops, secattr text_ops)); -+ #define SecuritySecattrIndexId 3402 -+ - DECLARE_UNIQUE_INDEX(pg_foreign_data_wrapper_oid_index, 112, on pg_foreign_data_wrapper using btree(oid oid_ops)); - #define ForeignDataWrapperOidIndexId 112 - -diff -Nrpc base/src/include/catalog/pg_attribute.h sepgsql/src/include/catalog/pg_attribute.h -*** base/src/include/catalog/pg_attribute.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/catalog/pg_attribute.h Thu Sep 10 15:29:52 2009 -*************** DATA(insert ( 1247 cmin 29 0 4 -4 0 -*** 276,281 **** ---- 276,282 ---- - DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); - DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); - DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); -+ DATA(insert ( 1247 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); - - /* ---------------- - * pg_proc -*************** DATA(insert ( 1255 cmin 29 0 4 -4 0 -*** 340,345 **** ---- 341,347 ---- - DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); - DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); - DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); -+ DATA(insert ( 1255 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); - - /* ---------------- - * pg_attribute -*************** DATA(insert ( 1249 cmin 29 0 4 -4 0 -*** 390,395 **** ---- 392,398 ---- - DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); - DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); - DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); -+ DATA(insert ( 1249 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); - - /* ---------------- - * pg_class -*************** DATA(insert ( 1259 cmin 29 0 4 -4 0 -*** 454,459 **** ---- 457,463 ---- - DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); - DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); - DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); -+ DATA(insert ( 1259 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); - - /* ---------------- - * pg_index -diff -Nrpc base/src/include/catalog/pg_conversion_fn.h sepgsql/src/include/catalog/pg_conversion_fn.h -*** base/src/include/catalog/pg_conversion_fn.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/catalog/pg_conversion_fn.h Thu Sep 17 22:10:19 2009 -*************** -*** 17,23 **** - extern Oid ConversionCreate(const char *conname, Oid connamespace, - Oid conowner, - int32 conforencoding, int32 contoencoding, -! Oid conproc, bool def); - extern void RemoveConversionById(Oid conversionOid); - extern Oid FindConversion(const char *conname, Oid connamespace); - extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); ---- 17,23 ---- - extern Oid ConversionCreate(const char *conname, Oid connamespace, - Oid conowner, - int32 conforencoding, int32 contoencoding, -! Oid conproc, Oid consecid, bool def); - extern void RemoveConversionById(Oid conversionOid); - extern Oid FindConversion(const char *conname, Oid connamespace); - extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); -diff -Nrpc base/src/include/catalog/pg_namespace.h sepgsql/src/include/catalog/pg_namespace.h -*** base/src/include/catalog/pg_namespace.h Sat Jan 3 12:25:21 2009 ---- sepgsql/src/include/catalog/pg_namespace.h Wed Jul 15 19:35:52 2009 -*************** DESCR("standard public schema"); -*** 77,82 **** - /* - * prototypes for functions in pg_namespace.c - */ -! extern Oid NamespaceCreate(const char *nspName, Oid ownerId); - - #endif /* PG_NAMESPACE_H */ ---- 77,82 ---- - /* - * prototypes for functions in pg_namespace.c - */ -! extern Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid nspsecid); - - #endif /* PG_NAMESPACE_H */ -diff -Nrpc base/src/include/catalog/pg_proc.h sepgsql/src/include/catalog/pg_proc.h -*** base/src/include/catalog/pg_proc.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/catalog/pg_proc.h Wed Jul 15 20:03:59 2009 -*************** DATA(insert OID = 958 ( lo_tell PGN -*** 1226,1231 **** ---- 1226,1235 ---- - DESCR("large object position"); - DATA(insert OID = 1004 ( lo_truncate PGNSP PGUID 12 1 0 0 f f f t f v 2 0 23 "23 23" _null_ _null_ _null_ _null_ lo_truncate _null_ _null_ _null_ )); - DESCR("truncate large object"); -+ DATA(insert OID = 1029 ( lo_get_security PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "26" _null_ _null_ _null_ _null_ lo_get_security _null_ _null_ _null_ )); -+ DESCR("get security label of large object"); -+ DATA(insert OID = 1030 ( lo_set_security PGNSP PGUID 12 1 0 0 f f f t f v 2 0 16 "26 25" _null_ _null_ _null_ _null_ lo_set_security _null_ _null_ _null_ )); -+ DESCR("set security label of large object"); - - DATA(insert OID = 959 ( on_pl PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "600 628" _null_ _null_ _null_ _null_ on_pl _null_ _null_ _null_ )); - DESCR("point on line?"); -*************** DESCR("I/O"); -*** 4335,4340 **** ---- 4339,4361 ---- - DATA(insert OID = 2963 ( uuid_hash PGNSP PGUID 12 1 0 0 f f f t f i 1 0 23 "2950" _null_ _null_ _null_ _null_ uuid_hash _null_ _null_ _null_ )); - DESCR("hash"); - -+ /* Security attribute reclaimer */ -+ DATA(insert OID = 3411 ( security_reclaim_label PGNSP PGUID 12 1 0 0 f f f t f v 0 0 23 "" _null_ _null_ _null_ _null_ security_reclaim_label _null_ _null_ _null_ )); -+ DATA(insert OID = 3413 ( security_reclaim_label PGNSP PGUID 12 1 0 0 f f f t f v 1 0 23 "2205" _null_ _null_ _null_ _null_ security_reclaim_table_label _null_ _null_ _null_ )); -+ DATA(insert OID = 3415 ( security_label_to_secid PGNSP PGUID 12 1 0 0 f f f t f v 1 0 26 "2249" _null_ _null_ _null_ _null_ security_label_to_secid _null_ _null_ _null_ )); -+ -+ /* SE-PostgreSQL related functions */ -+ DATA(insert OID = 3416 ( sepgsql_getcon PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ sepgsql_getcon _null_ _null_ _null_ )); -+ DATA(insert OID = 3417 ( sepgsql_server_getcon PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ sepgsql_server_getcon _null_ _null_ _null_ )); -+ DATA(insert OID = 3418 ( sepgsql_get_user PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_user _null_ _null_ _null_ )); -+ DATA(insert OID = 3419 ( sepgsql_set_user PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_user _null_ _null_ _null_ )); -+ DATA(insert OID = 3420 ( sepgsql_get_role PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_role _null_ _null_ _null_ )); -+ DATA(insert OID = 3421 ( sepgsql_set_role PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_role _null_ _null_ _null_ )); -+ DATA(insert OID = 3422 ( sepgsql_get_type PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_type _null_ _null_ _null_ )); -+ DATA(insert OID = 3423 ( sepgsql_set_type PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_type _null_ _null_ _null_ )); -+ DATA(insert OID = 3424 ( sepgsql_get_range PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_range _null_ _null_ _null_ )); -+ DATA(insert OID = 3425 ( sepgsql_set_range PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_range _null_ _null_ _null_ )); -+ - /* enum related procs */ - DATA(insert OID = 3504 ( anyenum_in PGNSP PGUID 12 1 0 0 f f f t f i 1 0 3500 "2275" _null_ _null_ _null_ _null_ anyenum_in _null_ _null_ _null_ )); - DESCR("I/O"); -diff -Nrpc base/src/include/catalog/pg_proc_fn.h sepgsql/src/include/catalog/pg_proc_fn.h -*** base/src/include/catalog/pg_proc_fn.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/catalog/pg_proc_fn.h Wed Jul 15 19:37:35 2009 -*************** extern Oid ProcedureCreate(const char *p -*** 37,43 **** - List *parameterDefaults, - Datum proconfig, - float4 procost, -! float4 prorows); - - extern bool function_parse_error_transpose(const char *prosrc); - ---- 37,44 ---- - List *parameterDefaults, - Datum proconfig, - float4 procost, -! float4 prorows, -! Node *proseclabel); - - extern bool function_parse_error_transpose(const char *prosrc); - -diff -Nrpc base/src/include/catalog/pg_security.h sepgsql/src/include/catalog/pg_security.h -*** base/src/include/catalog/pg_security.h Thu Jan 1 09:00:00 1970 ---- sepgsql/src/include/catalog/pg_security.h Thu Sep 10 15:18:03 2009 -*************** -*** 0 **** ---- 1,101 ---- -+ /* -+ * src/include/catalog/pg_security.h -+ * Definition of the security label relation (pg_security) -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+ #ifndef PG_SECURITY_H -+ #define PG_SECURITY_H -+ -+ #include "catalog/genbki.h" -+ -+ #include "access/htup.h" -+ #include "nodes/parsenodes.h" -+ #include "utils/acl.h" -+ #include "utils/relcache.h" -+ -+ #define SecurityRelationId 3400 -+ -+ CATALOG(pg_security,3400) BKI_SHARED_RELATION BKI_WITHOUT_OIDS -+ { -+ /* Identifier of the security attribute */ -+ Oid secid; -+ -+ /* OID of the database which referes the entry */ -+ Oid datid; -+ -+ /* OID of the table which refers the entry */ -+ Oid relid; -+ -+ /* See the SECKIND_SECURITY_* definition */ -+ char seckind; -+ -+ /* Text representation of security attribute */ -+ text secattr; -+ } FormData_pg_security; -+ -+ /* -+ * Form_pg_security corresponds to a pointer to a tuple with -+ * the format of pg_security relation. -+ */ -+ typedef FormData_pg_security *Form_pg_security; -+ -+ /* -+ * Compiler constants for pg_security -+ */ -+ #define Natts_pg_security 5 -+ #define Anum_pg_security_secid 1 -+ #define Anum_pg_security_datid 2 -+ #define Anum_pg_security_relid 3 -+ #define Anum_pg_security_seckind 4 -+ #define Anum_pg_security_secattr 5 -+ -+ /* -+ * Compiler constants for pg_security.seckind -+ */ -+ #define SECKIND_SECURITY_LABEL 'l' -+ -+ /* -+ * Functions to translate between security label and identifier -+ */ -+ extern void -+ securityPostBootstrapingMode(void); -+ -+ extern void -+ securityOnCreateDatabase(Oid src_datid, Oid dst_datid); -+ -+ extern void -+ securityOnDropDatabase(Oid datid); -+ -+ extern bool -+ securityTupleDescHasSecid(Oid relid, char relkind); -+ -+ extern Oid -+ securityRawSecLabelIn(Oid relid, char *seclabel); -+ -+ extern char * -+ securityRawSecLabelOut(Oid relid, Oid secid); -+ -+ extern Oid -+ securityTransSecLabelIn(Oid relid, char *seclabel); -+ -+ extern char * -+ securityTransSecLabelOut(Oid relid, Oid secid); -+ -+ extern Datum -+ securitySysattSecLabelOut(Oid relid, HeapTuple tuple); -+ -+ extern void -+ securityReclaimOnDropTable(Oid relid); -+ -+ extern Datum -+ security_reclaim_label(PG_FUNCTION_ARGS); -+ -+ extern Datum -+ security_reclaim_table_label(PG_FUNCTION_ARGS); -+ -+ extern Datum -+ security_label_to_secid(PG_FUNCTION_ARGS); -+ -+ #endif /* PG_SECURITY_H */ -diff -Nrpc base/src/include/catalog/toasting.h sepgsql/src/include/catalog/toasting.h -*** base/src/include/catalog/toasting.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/catalog/toasting.h Wed Jul 15 19:30:50 2009 -*************** DECLARE_TOAST(pg_database, 2844, 2845); -*** 58,62 **** ---- 58,65 ---- - DECLARE_TOAST(pg_shdescription, 2846, 2847); - #define PgShdescriptionToastTable 2846 - #define PgShdescriptionToastIndex 2847 -+ DECLARE_TOAST(pg_security, 3403, 3404); -+ #define PgSecurityToastTable 3403 -+ #define PgSecurityToastIndex 3404 - - #endif /* TOASTING_H */ -diff -Nrpc base/src/include/commands/alter.h sepgsql/src/include/commands/alter.h -*** base/src/include/commands/alter.h Sat Jan 3 12:25:21 2009 ---- sepgsql/src/include/commands/alter.h Wed Jul 15 19:37:35 2009 -*************** -*** 19,23 **** ---- 19,24 ---- - extern void ExecRenameStmt(RenameStmt *stmt); - extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); - extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); -+ extern void ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt); - - #endif /* ALTER_H */ -diff -Nrpc base/src/include/commands/dbcommands.h sepgsql/src/include/commands/dbcommands.h -*** base/src/include/commands/dbcommands.h Sat Jan 3 12:25:21 2009 ---- sepgsql/src/include/commands/dbcommands.h Wed Jul 15 19:37:35 2009 -*************** extern void RenameDatabase(const char *o -*** 58,63 **** ---- 58,64 ---- - extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel); - extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt); - extern void AlterDatabaseOwner(const char *dbname, Oid newOwnerId); -+ extern void AlterDatabaseSecLabel(const char *dbname, DefElem *seclabel); - - extern Oid get_database_oid(const char *dbname); - extern char *get_database_name(Oid dbid); -diff -Nrpc base/src/include/commands/defrem.h sepgsql/src/include/commands/defrem.h -*** base/src/include/commands/defrem.h Thu Apr 9 00:13:21 2009 ---- sepgsql/src/include/commands/defrem.h Wed Jul 15 19:37:35 2009 -*************** extern void SetFunctionArgType(Oid funcO -*** 53,58 **** ---- 53,59 ---- - extern void RenameFunction(List *name, List *argtypes, const char *newname); - extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId); - extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId); -+ extern void AlterFunctionSecLabel(List *name, List *argtypes, DefElem *seclabel); - extern void AlterFunction(AlterFunctionStmt *stmt); - extern void CreateCast(CreateCastStmt *stmt); - extern void DropCast(DropCastStmt *stmt); -diff -Nrpc base/src/include/commands/schemacmds.h sepgsql/src/include/commands/schemacmds.h -*** base/src/include/commands/schemacmds.h Sat Jan 3 12:25:21 2009 ---- sepgsql/src/include/commands/schemacmds.h Wed Jul 15 19:37:35 2009 -*************** extern void RemoveSchemaById(Oid schemaO -*** 26,30 **** ---- 26,31 ---- - extern void RenameSchema(const char *oldname, const char *newname); - extern void AlterSchemaOwner(const char *name, Oid newOwnerId); - extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId); -+ extern void AlterSchemaSecLabel(const char *name, DefElem *seclabel); - - #endif /* SCHEMACMDS_H */ -diff -Nrpc base/src/include/commands/tablecmds.h sepgsql/src/include/commands/tablecmds.h -*** base/src/include/commands/tablecmds.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/commands/tablecmds.h Wed Jul 15 19:37:35 2009 -*************** extern void AlterRelationNamespaceIntern -*** 35,40 **** ---- 35,43 ---- - Oid oldNspOid, Oid newNspOid, - bool hasDependEntry); - -+ extern void AlterRelationSecLabel(RangeVar *relation, const char *attname, -+ ObjectType objtype, DefElem *seclabel); -+ - extern void CheckTableNotInUse(Relation rel, const char *stmt); - - extern void ExecuteTruncate(TruncateStmt *stmt); -diff -Nrpc base/src/include/executor/executor.h sepgsql/src/include/executor/executor.h -*** base/src/include/executor/executor.h Sun Sep 6 19:40:49 2009 ---- sepgsql/src/include/executor/executor.h Wed Sep 9 13:14:37 2009 -*************** extern TupleHashEntry FindTupleHashEntry -*** 130,136 **** - /* - * prototypes from functions in execJunk.c - */ -! extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, - TupleTableSlot *slot); - extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, - TupleDesc cleanTupType, ---- 130,136 ---- - /* - * prototypes from functions in execJunk.c - */ -! extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel, - TupleTableSlot *slot); - extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, - TupleDesc cleanTupType, -*************** extern void InitResultRelInfo(ResultRelI -*** 163,168 **** ---- 163,169 ---- - bool doInstrument); - extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); - extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); -+ extern bool ExecContextForcesSecids(PlanState *planstate, bool *hassecids); - extern void ExecConstraints(ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, EState *estate); - extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, -*************** extern void ExecInitScanTupleSlot(EState -*** 216,223 **** - extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); - extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, - TupleDesc tupType); -! extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); -! extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); - extern TupleDesc ExecTypeFromExprList(List *exprList); - extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); - ---- 217,224 ---- - extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); - extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, - TupleDesc tupType); -! extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hasseclabel); -! extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hasseclabel); - extern TupleDesc ExecTypeFromExprList(List *exprList); - extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); - -diff -Nrpc base/src/include/executor/tuptable.h sepgsql/src/include/executor/tuptable.h -*** base/src/include/executor/tuptable.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/executor/tuptable.h Wed Jul 15 19:38:52 2009 -*************** typedef struct TupleTableSlot -*** 127,132 **** ---- 127,133 ---- - MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */ - HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */ - long tts_off; /* saved state for slot_deform_tuple */ -+ Datum tts_seclabel; /* temp storage for the given security_label */ - } TupleTableSlot; - - #define TTS_HAS_PHYSICAL_TUPLE(slot) \ -diff -Nrpc base/src/include/libpq/be-fsstubs.h sepgsql/src/include/libpq/be-fsstubs.h -*** base/src/include/libpq/be-fsstubs.h Sat Jan 3 12:25:21 2009 ---- sepgsql/src/include/libpq/be-fsstubs.h Wed Jul 15 19:48:58 2009 -*************** extern Datum lo_tell(PG_FUNCTION_ARGS); -*** 37,42 **** ---- 37,45 ---- - extern Datum lo_unlink(PG_FUNCTION_ARGS); - extern Datum lo_truncate(PG_FUNCTION_ARGS); - -+ extern Datum lo_get_security(PG_FUNCTION_ARGS); -+ extern Datum lo_set_security(PG_FUNCTION_ARGS); -+ - /* - * These are not fmgr-callable, but are available to C code. - * Probably these should have had the underscore-free names, -diff -Nrpc base/src/include/nodes/nodes.h sepgsql/src/include/nodes/nodes.h -*** base/src/include/nodes/nodes.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/nodes/nodes.h Wed Jul 15 19:37:35 2009 -*************** typedef enum NodeTag -*** 337,342 **** ---- 337,343 ---- - T_CreateUserMappingStmt, - T_AlterUserMappingStmt, - T_DropUserMappingStmt, -+ T_AlterSecLabelStmt, - - /* - * TAGS FOR PARSE TREE NODES (parsenodes.h) -diff -Nrpc base/src/include/nodes/parsenodes.h sepgsql/src/include/nodes/parsenodes.h -*** base/src/include/nodes/parsenodes.h Tue Dec 15 17:16:51 2009 ---- sepgsql/src/include/nodes/parsenodes.h Tue Dec 15 17:30: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 -*** 1334,1339 **** ---- 1336,1342 ---- - List *options; /* options from WITH clause */ - OnCommitAction oncommit; /* what do we do at COMMIT? */ - char *tablespacename; /* table space to use, or NULL */ -+ Node *secLabel; /* explicitly specified security label */ - } CreateStmt; - - /* ---------- -*************** typedef struct CreateSeqStmt -*** 1638,1643 **** ---- 1641,1647 ---- - NodeTag type; - RangeVar *sequence; /* the sequence to create */ - List *options; -+ Node *secLabel; - } CreateSeqStmt; - - typedef struct AlterSeqStmt -*************** typedef struct AlterOwnerStmt -*** 1992,1997 **** ---- 1996,2015 ---- - char *newowner; /* the new owner */ - } AlterOwnerStmt; - -+ /* ---------------------- -+ * Alter Object Security Label Statement -+ * ---------------------- -+ */ -+ typedef struct AlterSecLabelStmt -+ { -+ NodeTag type; -+ ObjectType objectType; /* OBJECT_TABLE, OBJECT_COLUMN, etc */ -+ RangeVar *relation; /* in case it's a table */ -+ List *object; /* in case it's some other object */ -+ List *objarg; /* argument types, if applicable */ -+ char *subname; /* column name, if needed */ -+ Node *secLabel; /* the new security label */ -+ } AlterSecLabelStmt; - - /* ---------------------- - * Create Rule Statement -diff -Nrpc base/src/include/nodes/plannodes.h sepgsql/src/include/nodes/plannodes.h -*** base/src/include/nodes/plannodes.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/nodes/plannodes.h Wed Jul 15 19:39:56 2009 -*************** -*** 16,21 **** ---- 16,22 ---- - - #include "access/sdir.h" - #include "nodes/bitmapset.h" -+ #include "nodes/parsenodes.h" - #include "nodes/primnodes.h" - #include "storage/itemptr.h" - -*************** typedef struct Scan -*** 239,244 **** ---- 240,251 ---- - { - Plan plan; - Index scanrelid; /* relid is index into the range table */ -+ -+ /* -+ * Row-level access control stuff. Zero means we don't need -+ * to apply row-level access control on the Scan. -+ */ -+ uint32 rowlvPerms; - } Scan; - - /* ---------------- -diff -Nrpc base/src/include/nodes/relation.h sepgsql/src/include/nodes/relation.h -*** base/src/include/nodes/relation.h Thu Jun 18 10:20:52 2009 ---- sepgsql/src/include/nodes/relation.h Wed Jul 15 19:39:56 2009 -*************** typedef struct RelOptInfo -*** 383,388 **** ---- 383,397 ---- - * list just to avoid recomputing the best inner indexscan repeatedly for - * similar outer relations. See comments for InnerIndexscanInfo. - */ -+ -+ /* -+ * Permissions used in Row-level access control features both of DAC -+ * and MAC. The lower 16bit is used for DAC, and rest of upper bits -+ * are used for MAC. When rowlvPerms is zero, so it means we don't need -+ * to apply the row-level stuff on the relation in both of levels. -+ * It can be used as a hint for optimization stuff. -+ */ -+ uint32 rowlvPerms; - } RelOptInfo; - - /* -diff -Nrpc base/src/include/pg_config.h.in sepgsql/src/include/pg_config.h.in -*** base/src/include/pg_config.h.in Thu Apr 9 00:13:21 2009 ---- sepgsql/src/include/pg_config.h.in Wed Jul 15 19:35:52 2009 -*************** -*** 391,396 **** ---- 391,399 ---- - /* Define to 1 if you have the header file. */ - #undef HAVE_SECURITY_PAM_APPL_H - -+ /* Define to 1 if you enable SELinux support */ -+ #undef HAVE_SELINUX -+ - /* Define to 1 if you have the `setproctitle' function. */ - #undef HAVE_SETPROCTITLE - -diff -Nrpc base/src/include/security/rowlevel.h sepgsql/src/include/security/rowlevel.h -*** base/src/include/security/rowlevel.h Thu Jan 1 09:00:00 1970 ---- sepgsql/src/include/security/rowlevel.h Thu Jul 16 17:22:29 2009 -*************** -*** 0 **** ---- 1,44 ---- -+ /* -+ * src/include/security/rowlevel.h -+ * Definition of the facility of row-level access controls -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+ #ifndef ROWLEVEL_H -+ #define ROWLEVEL_H -+ -+ #include "access/htup.h" -+ #include "executor/tuptable.h" -+ #include "nodes/plannodes.h" -+ #include "utils/relcache.h" -+ -+ #define ROWLV_BYPASS_MODE 1 -+ #define ROWLV_FILTER_MODE 2 -+ #define ROWLV_ABORT_MODE 3 -+ -+ extern int -+ rowlvGetPerformingMode(void); -+ -+ extern int -+ rowlvSetPerformingMode(int mode); -+ -+ extern uint32 -+ rowlvSetupPermissions(RangeTblEntry *rte); -+ -+ extern bool -+ rowlvExecScanFilter(Scan *scan, Relation rel, TupleTableSlot *slot); -+ -+ extern void -+ rowlvExecScanAbort(Scan *scan, Relation rel, TupleTableSlot *slot); -+ -+ extern void -+ rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal); -+ -+ extern void -+ rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup); -+ -+ extern bool -+ rowlvCopyToTuple(Relation rel, HeapTuple tuple); -+ -+ #endif /* ROWLEVEL_H */ -diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepgsql.h -*** base/src/include/security/sepgsql.h Thu Jan 1 09:00:00 1970 ---- sepgsql/src/include/security/sepgsql.h Tue Dec 8 14:04:25 2009 -*************** -*** 0 **** ---- 1,777 ---- -+ /* -+ * src/include/security/sepgsql.h -+ * Headers of SE-PostgreSQL -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+ #ifndef SEPGSQL_H -+ #define SEPGSQL_H -+ -+ #include "access/htup.h" -+ #include "catalog/dependency.h" -+ #include "executor/execdesc.h" -+ #include "fmgr.h" -+ #include "nodes/parsenodes.h" -+ #include "storage/large_object.h" -+ #include "utils/relcache.h" -+ -+ #ifdef HAVE_SELINUX -+ -+ #include -+ -+ /* GUC parameter to turn on/off SE-PostgreSQL */ -+ extern bool sepostgresql_is_enabled; -+ -+ /* GUC parameter to turn on/off Row-level controls */ -+ extern bool sepostgresql_row_level; -+ -+ /* GUC parameter to turn on/off mcstrans */ -+ extern bool sepostgresql_use_mcstrans; -+ -+ /* Objject classes and permissions internally used */ -+ enum SepgsqlClasses -+ { -+ SEPG_CLASS_PROCESS = 0, -+ SEPG_CLASS_FILE, -+ SEPG_CLASS_DIR, -+ SEPG_CLASS_LNK_FILE, -+ SEPG_CLASS_CHR_FILE, -+ SEPG_CLASS_BLK_FILE, -+ SEPG_CLASS_SOCK_FILE, -+ SEPG_CLASS_FIFO_FILE, -+ SEPG_CLASS_DB_DATABASE, -+ SEPG_CLASS_DB_SCHEMA, -+ SEPG_CLASS_DB_TABLE, -+ SEPG_CLASS_DB_SEQUENCE, -+ SEPG_CLASS_DB_PROCEDURE, -+ SEPG_CLASS_DB_COLUMN, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_CLASS_MAX, -+ }; -+ -+ #define SEPG_PROCESS__TRANSITION (1<<0) -+ -+ #define SEPG_FILE__READ (1<<0) -+ #define SEPG_FILE__WRITE (1<<1) -+ #define SEPG_FILE__CREATE (1<<2) -+ #define SEPG_FILE__GETATTR (1<<3) -+ -+ #define SEPG_DIR__READ (SEPG_FILE__READ) -+ #define SEPG_DIR__WRITE (SEPG_FILE__WRITE) -+ #define SEPG_DIR__CREATE (SEPG_FILE__CREATE) -+ #define SEPG_DIR__GETATTR (SEPG_FILE__GETATTR) -+ -+ #define SEPG_LNK_FILE__READ (SEPG_FILE__READ) -+ #define SEPG_LNK_FILE__WRITE (SEPG_FILE__WRITE) -+ #define SEPG_LNK_FILE__CREATE (SEPG_FILE__CREATE) -+ #define SEPG_LNK_FILE__GETATTR (SEPG_FILE__GETATTR) -+ -+ #define SEPG_CHR_FILE__READ (SEPG_FILE__READ) -+ #define SEPG_CHR_FILE__WRITE (SEPG_FILE__WRITE) -+ #define SEPG_CHR_FILE__CREATE (SEPG_FILE__CREATE) -+ #define SEPG_CHR_FILE__GETATTR (SEPG_FILE__GETATTR) -+ -+ #define SEPG_BLK_FILE__READ (SEPG_FILE__READ) -+ #define SEPG_BLK_FILE__WRITE (SEPG_FILE__WRITE) -+ #define SEPG_BLK_FILE__CREATE (SEPG_FILE__CREATE) -+ #define SEPG_BLK_FILE__GETATTR (SEPG_FILE__GETATTR) -+ -+ #define SEPG_SOCK_FILE__READ (SEPG_FILE__READ) -+ #define SEPG_SOCK_FILE__WRITE (SEPG_FILE__WRITE) -+ #define SEPG_SOCK_FILE__CREATE (SEPG_FILE__CREATE) -+ #define SEPG_SOCK_FILE__GETATTR (SEPG_FILE__GETATTR) -+ -+ #define SEPG_FIFO_FILE__READ (SEPG_FILE__READ) -+ #define SEPG_FIFO_FILE__WRITE (SEPG_FILE__WRITE) -+ #define SEPG_FIFO_FILE__CREATE (SEPG_FILE__CREATE) -+ #define SEPG_FIFO_FILE__GETATTR (SEPG_FILE__GETATTR) -+ -+ #define SEPG_DB_DATABASE__CREATE (1<<0) -+ #define SEPG_DB_DATABASE__DROP (1<<1) -+ #define SEPG_DB_DATABASE__GETATTR (1<<2) -+ #define SEPG_DB_DATABASE__SETATTR (1<<3) -+ #define SEPG_DB_DATABASE__RELABELFROM (1<<4) -+ #define SEPG_DB_DATABASE__RELABELTO (1<<5) -+ #define SEPG_DB_DATABASE__ACCESS (1<<6) -+ #define SEPG_DB_DATABASE__INSTALL_MODULE (1<<7) -+ #define SEPG_DB_DATABASE__LOAD_MODULE (1<<8) -+ #define SEPG_DB_DATABASE__SUPERUSER (1<<9) -+ -+ #define SEPG_DB_SCHEMA__CREATE (SEPG_DB_DATABASE__CREATE) -+ #define SEPG_DB_SCHEMA__DROP (SEPG_DB_DATABASE__DROP) -+ #define SEPG_DB_SCHEMA__GETATTR (SEPG_DB_DATABASE__GETATTR) -+ #define SEPG_DB_SCHEMA__SETATTR (SEPG_DB_DATABASE__SETATTR) -+ #define SEPG_DB_SCHEMA__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+ #define SEPG_DB_SCHEMA__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+ #define SEPG_DB_SCHEMA__SEARCH (1<<6) -+ #define SEPG_DB_SCHEMA__ADD_NAME (1<<7) -+ #define SEPG_DB_SCHEMA__REMOVE_NAME (1<<8) -+ -+ #define SEPG_DB_TABLE__CREATE (SEPG_DB_DATABASE__CREATE) -+ #define SEPG_DB_TABLE__DROP (SEPG_DB_DATABASE__DROP) -+ #define SEPG_DB_TABLE__GETATTR (SEPG_DB_DATABASE__GETATTR) -+ #define SEPG_DB_TABLE__SETATTR (SEPG_DB_DATABASE__SETATTR) -+ #define SEPG_DB_TABLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+ #define SEPG_DB_TABLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+ #define SEPG_DB_TABLE__SELECT (1<<6) -+ #define SEPG_DB_TABLE__UPDATE (1<<7) -+ #define SEPG_DB_TABLE__INSERT (1<<8) -+ #define SEPG_DB_TABLE__DELETE (1<<9) -+ #define SEPG_DB_TABLE__LOCK (1<<10) -+ #define SEPG_DB_TABLE__REFERENCE (1<<11) -+ -+ #define SEPG_DB_SEQUENCE__CREATE (SEPG_DB_DATABASE__CREATE) -+ #define SEPG_DB_SEQUENCE__DROP (SEPG_DB_DATABASE__DROP) -+ #define SEPG_DB_SEQUENCE__GETATTR (SEPG_DB_DATABASE__GETATTR) -+ #define SEPG_DB_SEQUENCE__SETATTR (SEPG_DB_DATABASE__SETATTR) -+ #define SEPG_DB_SEQUENCE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+ #define SEPG_DB_SEQUENCE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+ #define SEPG_DB_SEQUENCE__GET_VALUE (1<<6) -+ #define SEPG_DB_SEQUENCE__NEXT_VALUE (1<<7) -+ #define SEPG_DB_SEQUENCE__SET_VALUE (1<<8) -+ -+ #define SEPG_DB_PROCEDURE__CREATE (SEPG_DB_DATABASE__CREATE) -+ #define SEPG_DB_PROCEDURE__DROP (SEPG_DB_DATABASE__DROP) -+ #define SEPG_DB_PROCEDURE__GETATTR (SEPG_DB_DATABASE__GETATTR) -+ #define SEPG_DB_PROCEDURE__SETATTR (SEPG_DB_DATABASE__SETATTR) -+ #define SEPG_DB_PROCEDURE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+ #define SEPG_DB_PROCEDURE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+ #define SEPG_DB_PROCEDURE__EXECUTE (1<<6) -+ #define SEPG_DB_PROCEDURE__ENTRYPOINT (1<<7) -+ #define SEPG_DB_PROCEDURE__INSTALL (1<<8) -+ #define SEPG_DB_PROCEDURE__UNTRUSTED (1<<9) -+ -+ #define SEPG_DB_COLUMN__CREATE (SEPG_DB_DATABASE__CREATE) -+ #define SEPG_DB_COLUMN__DROP (SEPG_DB_DATABASE__DROP) -+ #define SEPG_DB_COLUMN__GETATTR (SEPG_DB_DATABASE__GETATTR) -+ #define SEPG_DB_COLUMN__SETATTR (SEPG_DB_DATABASE__SETATTR) -+ #define SEPG_DB_COLUMN__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+ #define SEPG_DB_COLUMN__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+ #define SEPG_DB_COLUMN__SELECT (1<<6) -+ #define SEPG_DB_COLUMN__UPDATE (1<<7) -+ #define SEPG_DB_COLUMN__INSERT (1<<8) -+ #define SEPG_DB_COLUMN__REFERENCE (1<<9) -+ -+ #define SEPG_DB_TUPLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+ #define SEPG_DB_TUPLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+ #define SEPG_DB_TUPLE__SELECT (SEPG_DB_DATABASE__GETATTR) -+ #define SEPG_DB_TUPLE__UPDATE (SEPG_DB_DATABASE__SETATTR) -+ #define SEPG_DB_TUPLE__INSERT (SEPG_DB_DATABASE__CREATE) -+ #define SEPG_DB_TUPLE__DELETE (SEPG_DB_DATABASE__DROP) -+ -+ #define SEPG_DB_BLOB__CREATE (SEPG_DB_DATABASE__CREATE) -+ #define SEPG_DB_BLOB__DROP (SEPG_DB_DATABASE__DROP) -+ #define SEPG_DB_BLOB__GETATTR (SEPG_DB_DATABASE__GETATTR) -+ #define SEPG_DB_BLOB__SETATTR (SEPG_DB_DATABASE__SETATTR) -+ #define SEPG_DB_BLOB__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+ #define SEPG_DB_BLOB__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+ #define SEPG_DB_BLOB__READ (1<<6) -+ #define SEPG_DB_BLOB__WRITE (1<<7) -+ #define SEPG_DB_BLOB__IMPORT (1<<8) -+ #define SEPG_DB_BLOB__EXPORT (1<<9) -+ -+ /* -+ * sepgsql_sid_t : alternative representation of security context -+ */ -+ typedef struct { -+ Oid relid; -+ Oid secid; -+ } sepgsql_sid_t; -+ -+ #define SidIsValid(sid) (OidIsValid((sid).relid) && OidIsValid((sid).secid)) -+ -+ /* -+ * avc.c : userspace access vector caches -+ */ -+ -+ /* Hook to record audit logs */ -+ typedef void (*sepgsqlAvcAuditHook_t)(bool denied, -+ const char *scontext, -+ const char *tcontext, -+ const char *tclass, -+ const char *permissions, -+ const char *audit_name); -+ extern PGDLLIMPORT sepgsqlAvcAuditHook_t sepgsqlAvcAuditHook; -+ -+ extern Size sepgsqlShmemSize(void); -+ extern void sepgsqlAvcInitialize(void); -+ -+ extern bool sepgsqlGetEnforce(void); -+ extern int sepgsqlSetEnforce(int new_mode); -+ extern void sepgsqlAvcReset(void); -+ extern void sepgsqlAvcSwitchClient(const char *scontext); -+ -+ extern bool -+ sepgsqlClientHasPerms(sepgsql_sid_t tsid, -+ uint16 tclass, uint32 required, -+ const char *audit_name, bool abort); -+ extern sepgsql_sid_t -+ sepgsqlClientCreateSecid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid); -+ -+ extern security_context_t -+ sepgsqlClientCreateLabel(sepgsql_sid_t tsid, uint16 tclass); -+ -+ extern bool -+ sepgsqlComputePerms(security_context_t scontext, -+ security_context_t tcontext, -+ uint16 tclass, uint32 required, -+ const char *audit_name, bool abort); -+ -+ extern security_context_t -+ sepgsqlComputeCreate(security_context_t scontext, -+ security_context_t tcontext, -+ uint16 tclass); -+ -+ extern void sepgsqlReceiverMain(void); -+ -+ /* -+ * bridge.c : new style security hooks -+ */ -+ -+ /* pg_attribute */ -+ extern Oid -+ sepgsql_attribute_create(Oid relOid, ColumnDef *cdef); -+ extern void -+ sepgsql_attribute_alter(Oid relOid, const char *attname); -+ extern void -+ sepgsql_attribute_drop(Oid relOid, AttrNumber attnum); -+ extern void -+ sepgsql_attribute_grant(Oid relOid, AttrNumber attnum); -+ extern Oid -+ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, DefElem *newLabel); -+ -+ /* pg_cast */ -+ extern Oid -+ sepgsql_cast_create(Oid sourceTypOid, Oid targetTypOid, Oid funcOid); -+ extern void -+ sepgsql_cast_drop(Oid castOid); -+ -+ /* pg_class */ -+ extern Oid * -+ sepgsql_relation_create(const char *relName, -+ char relkind, -+ TupleDesc tupDesc, -+ Oid nspOid, -+ DefElem *relLabel, -+ List *colList, -+ bool createAs, -+ bool permission); -+ extern Oid * -+ sepgsql_relation_copy(Relation src); -+ extern void -+ sepgsql_relation_alter(Oid relOid, const char *newName, Oid newNsp); -+ extern void -+ sepgsql_relation_drop(Oid relOid); -+ extern void -+ sepgsql_relation_grant(Oid relOid); -+ extern Oid -+ sepgsql_relation_relabel(Oid relOid, DefElem *newLabel); -+ extern void -+ sepgsql_relation_get_transaction_id(Oid relOid); -+ extern void -+ sepgsql_relation_copy_definition(Oid relOid); -+ extern void -+ sepgsql_relation_truncate(Relation rel); -+ extern void -+ sepgsql_relation_references(Relation rel, int16 *attnums, int natts); -+ extern void -+ sepgsql_relation_lock(Oid relOid); -+ extern void -+ sepgsql_view_replace(Oid viewOid); -+ extern void -+ sepgsql_index_create(Oid relOid, Oid nspOid, bool check_rights); -+ extern void -+ sepgsql_sequence_get_value(Oid seqOid); -+ extern void -+ sepgsql_sequence_next_value(Oid seqOid); -+ extern void -+ sepgsql_sequence_set_value(Oid seqOid); -+ -+ /* pg_conversion */ -+ extern Oid -+ sepgsql_conversion_create(const char *convName, Oid nspOid, Oid procOid); -+ extern void -+ sepgsql_conversion_alter(Oid convOid, const char *newName); -+ extern void -+ sepgsql_conversion_drop(Oid convOid); -+ -+ /* pg_database */ -+ extern Oid -+ sepgsql_database_create(const char *datName, DefElem *newLabel); -+ extern void -+ sepgsql_database_alter(Oid datOid); -+ extern void -+ sepgsql_database_drop(Oid datOid); -+ extern Oid -+ sepgsql_database_relabel(Oid datOid, DefElem *newLabel); -+ extern void -+ sepgsql_database_grant(Oid datOid); -+ extern void -+ sepgsql_database_access(Oid datOid); -+ extern bool -+ sepgsql_database_superuser(Oid datOid); -+ extern void -+ sepgsql_database_load_module(Oid datOid, const char *filename); -+ -+ /* pg_foreign_data_wrapper */ -+ extern Oid -+ sepgsql_fdw_create(const char *fdwName, Oid fdwValidator); -+ extern void -+ sepgsql_fdw_alter(Oid fdwOid, Oid newValidator); -+ extern void -+ sepgsql_fdw_drop(Oid fdwOid); -+ extern void -+ sepgsql_fdw_grant(Oid fdwOid); -+ -+ /* pg_foreign_server */ -+ extern Oid -+ sepgsql_foreign_server_create(const char *fsrvName); -+ extern void -+ sepgsql_foreign_server_alter(Oid fsrvOid); -+ extern void -+ sepgsql_foreign_server_drop(Oid fsrvOid); -+ extern void -+ sepgsql_foreign_server_grant(Oid fsrvOid); -+ -+ /* pg_language */ -+ extern Oid -+ sepgsql_language_create(const char *langName, Oid handlerOid, Oid validatorOid); -+ extern void -+ sepgsql_language_alter(Oid langOid); -+ extern void -+ sepgsql_language_drop(Oid langOid); -+ extern void -+ sepgsql_language_grant(Oid langOid); -+ -+ /* pg_largeobject */ -+ extern Oid -+ sepgsql_largeobject_create(Oid loid, DefElem *newLabel); -+ extern void -+ sepgsql_largeobject_alter(Oid loid, Oid newOwner); -+ extern void -+ sepgsql_largeobject_drop(Oid loid, bool dacSkip); -+ extern void -+ sepgsql_largeobject_read(Oid loid); -+ extern void -+ sepgsql_largeobject_write(Oid loid); -+ extern void -+ sepgsql_largeobject_export(Oid loid, const char *filename); -+ extern Oid -+ sepgsql_largeobject_import(Oid loid, const char *filename, DefElem *newLabel); -+ -+ /* pg_namespace */ -+ extern Oid -+ sepgsql_schema_create(const char *nspName, bool isTemp, DefElem *newLabel); -+ extern void -+ sepgsql_schema_alter(Oid nspOid); -+ extern void -+ sepgsql_schema_drop(Oid nspOid); -+ extern Oid -+ sepgsql_schema_relabel(Oid nspOid, DefElem *newLabel); -+ extern void -+ sepgsql_schema_grant(Oid nspOid); -+ extern bool -+ sepgsql_schema_search(Oid nspOid, bool abort); -+ -+ /* pg_opclass */ -+ extern Oid -+ sepgsql_opclass_create(const char *opcName, Oid nspOid); -+ extern void -+ sepgsql_opclass_alter(Oid opcOid, const char *newName); -+ extern void -+ sepgsql_opclass_drop(Oid opcOid); -+ -+ /* pg_opfamily */ -+ extern Oid -+ sepgsql_opfamily_create(const char *opfName, Oid nspOid); -+ extern void -+ sepgsql_opfamily_alter(Oid opfOid, const char *newName); -+ extern void -+ sepgsql_opfamily_drop(Oid opfOid); -+ extern void -+ sepgsql_opfamily_add_operator(Oid opfOid, Oid operOid); -+ extern void -+ sepgsql_opfamily_add_procedure(Oid opfOid, Oid procOid); -+ -+ /* pg_operator */ -+ extern Oid -+ sepgsql_operator_create(const char *oprName, Oid oprOid, Oid nspOid, -+ Oid codeFn, Oid restFn, Oid joinFn); -+ extern void -+ sepgsql_operator_alter(Oid oprOid); -+ extern void -+ sepgsql_operator_drop(Oid oprOid); -+ -+ /* pg_proc */ -+ extern Oid -+ sepgsql_proc_create(const char *procName, HeapTuple oldTup, -+ Oid nspOid, Oid langOid, DefElem *newLabel); -+ extern void -+ sepgsql_proc_alter(Oid procOid, const char *newName, Oid newNsp); -+ extern void -+ sepgsql_proc_drop(Oid procOid); -+ extern Oid -+ sepgsql_proc_relabel(Oid procOid, DefElem *newLabel); -+ extern void -+ sepgsql_proc_grant(Oid procOid); -+ extern void -+ sepgsql_proc_execute(Oid procOid); -+ extern bool -+ sepgsql_proc_hint_inlined(HeapTuple protup); -+ extern void -+ sepgsql_proc_entrypoint(FmgrInfo *flinfo, HeapTuple protup); -+ -+ /* pg_rewrite */ -+ extern void -+ sepgsql_rule_create(Oid relOid, const char *ruleName); -+ extern void -+ sepgsql_rule_drop(Oid relOid, const char *ruleName); -+ -+ /* pg_trigger */ -+ extern void -+ sepgsql_trigger_create(Oid relOid, const char *trigName, Oid procOid); -+ extern void -+ sepgsql_trigger_alter(Oid relOid, const char *trigName); -+ extern void -+ sepgsql_trigger_drop(Oid relOid, const char *trigName); -+ -+ /* pg_ts_config */ -+ extern Oid -+ sepgsql_ts_config_create(const char *cfgName, Oid nspOid); -+ extern void -+ sepgsql_ts_config_alter(Oid cfgOid, const char *newName); -+ extern void -+ sepgsql_ts_config_drop(Oid cfgOid); -+ -+ /* pg_ts_dict */ -+ extern Oid -+ sepgsql_ts_dict_create(const char *dictName, Oid nspOid); -+ extern void -+ sepgsql_ts_dict_alter(Oid dictOid, const char *newName); -+ extern void -+ sepgsql_ts_dict_drop(Oid dictOid); -+ -+ /* pg_ts_parser */ -+ extern Oid -+ sepgsql_ts_parser_create(const char *prsName, Oid nspOid, -+ Oid startFn, Oid tokenFn, Oid sendFn, -+ Oid headlineFn, Oid lextypeFn); -+ extern void -+ sepgsql_ts_parser_alter(Oid prsOid, const char *newName); -+ extern void -+ sepgsql_ts_parser_drop(Oid prsOid); -+ -+ /* pg_ts_templace */ -+ extern Oid -+ sepgsql_ts_template_create(const char *tmplName, Oid nspOid, -+ Oid initFn, Oid lexizeFn); -+ extern void -+ sepgsql_ts_template_alter(Oid tmplOid, const char *newName); -+ extern void -+ sepgsql_ts_template_drop(Oid tmplOid); -+ -+ /* pg_type */ -+ extern Oid -+ sepgsql_type_create(const char *typName, HeapTuple oldTup, Oid nspOid, -+ Oid inputProc, Oid outputProc, Oid recvProc, Oid sendProc, -+ Oid modinProc, Oid modoutProc, Oid analyzeProc); -+ extern void -+ sepgsql_type_alter(Oid typOid, const char *newName, Oid newNsp); -+ extern void -+ sepgsql_type_drop(Oid typOid); -+ -+ /* misc objects */ -+ extern void -+ sepgsql_sysobj_drop(const ObjectAddress *object); -+ -+ /* filesystem objects */ -+ void -+ sepgsql_file_stat(const char *filename); -+ void -+ sepgsql_file_read(const char *filename); -+ void -+ sepgsql_file_write(const char *filename); -+ -+ /* -+ * checker.c : check permission on given queries -+ */ -+ extern void -+ sepgsqlCheckRTEPerms(RangeTblEntry *rte); -+ -+ extern void -+ sepgsqlCheckCopyTable(Relation rel, List *attnumlist, bool is_from); -+ -+ extern void -+ sepgsqlCheckSelectInto(Oid relaionId); -+ -+ extern bool -+ sepgsqlExecScan(Relation rel, HeapTuple tuple, uint32 required, bool abort); -+ -+ extern uint32 -+ sepgsqlSetupTuplePerms(RangeTblEntry *rte); -+ -+ extern void -+ sepgsqlHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal); -+ -+ extern void -+ sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup); -+ -+ /* -+ * core.c : core facilities -+ */ -+ extern security_context_t -+ sepgsqlGetServerLabel(void); -+ -+ extern security_context_t -+ sepgsqlGetClientLabel(void); -+ -+ extern security_context_t -+ sepgsqlSwitchClient(security_context_t new_client); -+ -+ extern bool -+ sepgsqlIsEnabled(void); -+ -+ extern void -+ sepgsqlInitialize(void); -+ -+ /* -+ * hooks.c : routines to check certain permissions -+ */ -+ extern void -+ sepgsqlCheckBlobCreate(Relation rel, HeapTuple lotup); -+ extern void -+ sepgsqlCheckBlobDrop(Relation rel, HeapTuple lotup); -+ extern void -+ sepgsqlCheckBlobRead(LargeObjectDesc *lobj); -+ extern void -+ sepgsqlCheckBlobWrite(LargeObjectDesc *lobj); -+ extern void -+ sepgsqlCheckBlobGetattr(HeapTuple tuple); -+ extern void -+ sepgsqlCheckBlobSetattr(HeapTuple tuple); -+ extern void -+ sepgsqlCheckBlobExport(LargeObjectDesc *lobj, const char *filename); -+ extern void -+ sepgsqlCheckBlobImport(LargeObjectDesc *lobj, const char *filename); -+ extern void -+ sepgsqlCheckBlobRelabel(HeapTuple oldtup, HeapTuple newtup); -+ -+ /* -+ * label.c : security label management -+ */ -+ extern bool -+ sepgsqlTupleDescHasSecid(Oid relid, char relkind); -+ -+ extern void sepgsqlSetDefaultSecid(Relation rel, HeapTuple tuple); -+ extern sepgsql_sid_t sepgsqlGetDefaultDatabaseSecid(void); -+ extern sepgsql_sid_t sepgsqlGetDefaultSchemaSecid(Oid database_oid); -+ extern sepgsql_sid_t sepgsqlGetDefaultSchemaTempSecid(Oid database_oid); -+ extern sepgsql_sid_t sepgsqlGetDefaultTableSecid(Oid namespace_oid); -+ extern sepgsql_sid_t sepgsqlGetDefaultSequenceSecid(Oid namespace_oid); -+ extern sepgsql_sid_t sepgsqlGetDefaultProcedureSecid(Oid namespace_oid); -+ extern sepgsql_sid_t sepgsqlGetDefaultColumnSecid(Oid table_oid); -+ extern sepgsql_sid_t sepgsqlGetDefaultTupleSecid(Oid table_oid); -+ extern sepgsql_sid_t sepgsqlGetDefaultBlobSecid(Oid database_oid); -+ -+ extern Oid *sepgsqlCreateTableColumns(CreateStmt *stmt, -+ const char *relname, Oid namespace_oid, -+ TupleDesc tupdesc, char relkind); -+ extern Oid *sepgsqlCopyTableColumns(Relation source); -+ -+ extern sepgsql_sid_t -+ sepgsqlGetTupleSecid(Oid tableOid, HeapTuple tuple, uint16 *tclass); -+ extern sepgsql_sid_t -+ sepgsqlGetSysobjSecid(Oid tableOid, Oid objectId, int32 objsubId, uint16 *tclass); -+ -+ extern char *sepgsqlTransSecLabelIn(char *seclabel); -+ extern char *sepgsqlTransSecLabelOut(char *seclabel); -+ extern char *sepgsqlRawSecLabelIn(char *seclabel); -+ extern char *sepgsqlRawSecLabelOut(char *seclabel); -+ extern char *sepgsqlSysattSecLabelOut(Oid relid, HeapTuple tuple); -+ -+ /* -+ * perms.c : SELinux permission related stuff -+ */ -+ extern uint16 sepgsqlFileObjectClass(int fdesc); -+ -+ extern uint16 sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple); -+ -+ extern security_class_t sepgsqlTransToExternalClass(uint16 tclass_in); -+ -+ extern void sepgsqlTransToInternalPerms(security_class_t tclass_ex, -+ struct av_decision *avd); -+ extern const char *sepgsqlGetClassString(uint16 tclass); -+ extern const char *sepgsqlGetPermString(uint16 tclass, uint32 permission); -+ -+ #else /* HAVE_SELINUX */ -+ -+ /* avc.c */ -+ #define sepgsqlShmemSize() (0) -+ -+ /* checker.c */ -+ #define sepgsqlCheckRTEPerms(a) do {} while(0) -+ #define sepgsqlCheckCopyTable(a,b,c) do {} while(0) -+ #define sepgsqlCheckSelectInto(a) do {} while(0) -+ #define sepgsqlExecScan(a,b,c) (true) -+ #define sepgsqlSetupTuplePerms(a) (0) -+ #define sepgsqlHeapTupleInsert(a,b,c) do {} while(0) -+ #define sepgsqlHeapTupleUpdate(a,b,c) do {} while(0) -+ -+ /* core.c */ -+ #define sepgsqlIsEnabled() (false) -+ #define sepgsqlInitialize() do {} while(0) -+ -+ /* bridge.c */ -+ #define sepgsql_attribute_create(a,b) (InvalidOid) -+ #define sepgsql_attribute_alter(a,b) do {} while(0) -+ #define sepgsql_attribute_drop(a,b) do {} while(0) -+ #define sepgsql_attribute_grant(a,b) do {} while(0) -+ #define sepgsql_attribute_relabel(a,b,c) (InvalidOid) -+ -+ #define sepgsql_cast_create(a,b,c) (InvalidOid) -+ #define sepgsql_cast_drop(a) (InvalidOid) -+ -+ #define sepgsql_relation_create(a,b,c,d,e,f) (NULL) -+ #define sepgsql_relation_copy(a) (NULL) -+ #define sepgsql_relation_alter(a,b,c) do {} while(0) -+ #define sepgsql_relation_drop(a) do {} while(0) -+ #define sepgsql_relation_grant(a) do {} while(0) -+ #define sepgsql_relation_relabel(a,b) do {} while(0) -+ #define sepgsql_relation_get_transaction_id(a) do {} while(0) -+ #define sepgsql_relation_copy_definition(a) do {} while(0) -+ #define sepgsql_relation_truncate(a) do {} while(0) -+ #define sepgsql_relation_references(a,b,c) do {} while(0) -+ #define sepgsql_relation_lock(a) do {} while(0) -+ #define sepgsql_view_replace(a) do {} while(0) -+ #define sepgsql_index_create(a,b,c) do {} while(0) -+ #define sepgsql_sequence_get_value(a) do {} while(0) -+ #define sepgsql_sequence_next_value(a) do {} while(0) -+ #define sepgsql_sequence_set_value(a) do {} while(0) -+ -+ #define sepgsql_conversion_create(a,b,c) do {} while(0) -+ #define sepgsql_conversion_alter(a,b) do {} while(0) -+ #define sepgsql_conversion_drop(a) do {} while(0) -+ -+ #define sepgsql_database_create(a,b) (InvalidOid) -+ #define sepgsql_database_alter(a) do {} while(0) -+ #define sepgsql_database_drop(a) do {} while(0) -+ #define sepgsql_database_relabel(a,b) (InvalidOid) -+ #define sepgsql_database_grant(a) do {} while(0) -+ #define sepgsql_database_access(a) do {} while(0) -+ #define sepgsql_database_superuser(a) (true) -+ #define sepgsql_database_load_module(a,b) do {} while(0) -+ -+ #define sepgsql_fdw_create(a,b) (InvalidOid) -+ #define sepgsql_fdw_alter(a,b) do {} while(0) -+ #define sepgsql_fdw_drop(a) do {} while(0) -+ #define sepgsql_fdw_grant(a) do {} while(0) -+ -+ #define sepgsql_foreign_server_create(a) (InvalidOid) -+ #define sepgsql_foreign_server_alter(a) do {} while(0) -+ #define sepgsql_foreign_server_drop(a) do {} while(0) -+ #define sepgsql_foreign_server_grant(a) do {} while(0) -+ -+ #define sepgsql_language_create(a,b,c) (InvalidOid) -+ #define sepgsql_language_alter(a) do {} while(0) -+ #define sepgsql_language_drop(a) do {} while(0) -+ #define sepgsql_language_grant(a) do {} while(0) -+ -+ #define sepgsql_largeobject_create(a,b) (InvalidOid) -+ #define sepgsql_largeobject_alter(a,b) do {} while(0) -+ #define sepgsql_largeobject_drop(a) do {} while(0) -+ #define sepgsql_largeobject_read(a) do {} while(0) -+ #define sepgsql_largeobject_write(a) do {} while(0) -+ #define sepgsql_largeobject_export(a,b) do {} while(0) -+ #define sepgsql_largeobject_import(a,b) (InvalidOid) -+ -+ #define sepgsql_schema_create(a,b,c) (InvalidOid) -+ #define sepgsql_schema_alter(a) do {} while(0) -+ #define sepgsql_schema_drop(a) do {} while(0) -+ #define sepgsql_schema_relabel(a,b) (InvalidOid) -+ #define sepgsql_schema_grant(a) do {} while(0) -+ #define sepgsql_schema_search(a,b) (true) -+ -+ #define sepgsql_opclass_create(a,b) (InvalidOid) -+ #define sepgsql_opclass_alter(a,b) do {} while(0) -+ #define sepgsql_opclass_drop(a) do {} while(0) -+ -+ #define sepgsql_opfamily_create(a,b) (InvalidOid) -+ #define sepgsql_opfamily_alter(a,b) do {} while(0) -+ #define sepgsql_opfamily_drop(a) do {} while(0) -+ #define sepgsql_opfamily_add_operator(a,b) do {} while(0) -+ #define sepgsql_opfamily_add_procedure(a,b) do {} while(0) -+ -+ #define sepgsql_operator_create(a,b,c,d,e,f) (InvalidOid) -+ #define sepgsql_operator_alter(a) do {} while(0) -+ #define sepgsql_operator_drop(a) do {} while(0) -+ -+ #define sepgsql_proc_create(a,b,c,d,e) (InvalidOid) -+ #define sepgsql_proc_alter(a,b,c) do {} while(0) -+ #define sepgsql_proc_drop(a) do {} while(0) -+ #define sepgsql_proc_relabel(a,b) (InvalidOid) -+ #define sepgsql_proc_grant(a) do {} while(0) -+ #define sepgsql_proc_execute(a) do {} while(0) -+ #define sepgsql_proc_hint_inlined(a) (true) -+ #define sepgsql_proc_entrypoint(a,b) do {} while(0) -+ -+ #define sepgsql_rule_create(a,b) do {} while(0) -+ #define sepgsql_rule_drop(a,b) do {} while(0) -+ -+ #define sepgsql_trigger_create(a,b,c) do {} while(0) -+ #define sepgsql_trigger_alter(a,b) do {} while(0) -+ #define sepgsql_trigger_drop(a,b) do {} while(0) -+ -+ #define sepgsql_ts_config_create(a,b) (InvalidOid) -+ #define sepgsql_ts_config_alter(a,b) do {} while(0) -+ #define sepgsql_ts_config_drop(a) do {} while(0) -+ -+ #define sepgsql_ts_config_create(a,b) (InvalidOid) -+ #define sepgsql_ts_config_alter(a,b) do {} while(0) -+ #define sepgsql_ts_config_drop(a) do {} while(0) -+ -+ #define sepgsql_ts_dict_create(a,b) (InvalidOid) -+ #define sepgsql_ts_dict_alter(a,b) do {} while(0) -+ #define sepgsql_ts_dict_drop(a) do {} while(0) -+ -+ #define sepgsql_ts_parser_create(a,b,c,d,e,f,g) (InvalidOid) -+ #define sepgsql_ts_parser_alter(a,b) do {} while(0) -+ #define sepgsql_ts_parser_drop(a) do {} while(0) -+ -+ #define sepgsql_ts_template_create(a,b,c,d) (InvalidOid) -+ #define sepgsql_ts_template_alter(a,b) do {} while(0) -+ #define sepgsql_ts_template_drop(a) do {} while(0) -+ -+ #define sepgsql_type_create(a,b,c,d,e,f,g,h,i,j) (InvalidOid) -+ #define sepgsql_type_alter(a,b,c) do {} while(0) -+ #define sepgsql_type_drop(a) do {} while(0) -+ -+ #define sepgsql_sysobj_drop(a) do {} while(0) -+ -+ #define sepgsql_file_stat(a) do {} while(0) -+ #define sepgsql_file_read(a) do {} while(0) -+ #define sepgsql_file_write(a) do {} while(0) -+ -+ /* label.c */ -+ #define sepgsqlTupleDescHasSecLabel(a,b) (false) -+ #define sepgsqlSetDefaultSecLabel(a,b) do {} while(0) -+ #define sepgsqlTransSecLabelIn(a) (a) -+ #define sepgsqlTransSecLabelOut(a) (a) -+ #define sepgsqlRawSecLabelIn(a) (a) -+ #define sepgsqlRawSecLabelOut(a) (a) -+ -+ #endif /* HAVE_SELINUX */ -+ -+ extern Datum sepgsql_getcon(PG_FUNCTION_ARGS); -+ extern Datum sepgsql_server_getcon(PG_FUNCTION_ARGS); -+ extern Datum sepgsql_get_user(PG_FUNCTION_ARGS); -+ extern Datum sepgsql_get_role(PG_FUNCTION_ARGS); -+ extern Datum sepgsql_get_type(PG_FUNCTION_ARGS); -+ extern Datum sepgsql_get_range(PG_FUNCTION_ARGS); -+ extern Datum sepgsql_set_user(PG_FUNCTION_ARGS); -+ extern Datum sepgsql_set_role(PG_FUNCTION_ARGS); -+ extern Datum sepgsql_set_type(PG_FUNCTION_ARGS); -+ extern Datum sepgsql_set_range(PG_FUNCTION_ARGS); -+ -+ #endif /* SEPGSQL_H */ -diff -Nrpc base/src/include/storage/fd.h sepgsql/src/include/storage/fd.h -*** base/src/include/storage/fd.h Tue Jan 13 09:22:28 2009 ---- sepgsql/src/include/storage/fd.h Wed Jul 15 19:48:58 2009 -*************** extern int FileWrite(File file, char *bu -*** 68,73 **** ---- 68,74 ---- - extern int FileSync(File file); - extern off_t FileSeek(File file, off_t offset, int whence); - extern int FileTruncate(File file, off_t offset); -+ extern int FileRawDescriptor(File file); - - /* Operations that allow use of regular stdio --- USE WITH CAUTION */ - extern FILE *AllocateFile(const char *name, const char *mode); -diff -Nrpc base/src/include/storage/large_object.h sepgsql/src/include/storage/large_object.h -*** base/src/include/storage/large_object.h Sat Jan 3 12:25:21 2009 ---- sepgsql/src/include/storage/large_object.h Wed Jul 15 19:48:58 2009 -*************** typedef struct LargeObjectDesc -*** 44,49 **** ---- 44,50 ---- - #define IFS_RDLOCK (1 << 0) - #define IFS_WRLOCK (1 << 1) - -+ Oid secid; /* security identifier of largeobject */ - } LargeObjectDesc; - - -*************** extern int inv_tell(LargeObjectDesc *obj -*** 79,83 **** ---- 80,86 ---- - extern int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes); - extern int inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes); - extern void inv_truncate(LargeObjectDesc *obj_desc, int len); -+ extern Oid inv_get_security(Oid loid); -+ extern void inv_set_security(Oid loid, Oid secid); - - #endif /* LARGE_OBJECT_H */ -diff -Nrpc base/src/include/storage/lwlock.h sepgsql/src/include/storage/lwlock.h -*** base/src/include/storage/lwlock.h Fri Mar 6 09:45:33 2009 ---- sepgsql/src/include/storage/lwlock.h Wed Jul 15 19:35:52 2009 -*************** typedef enum LWLockId -*** 67,72 **** ---- 67,73 ---- - AutovacuumLock, - AutovacuumScheduleLock, - SyncScanLock, -+ SepgsqlAvcLock, - /* Individual lock IDs end here */ - FirstBufMappingLock, - FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, -diff -Nrpc base/src/include/storage/proc.h sepgsql/src/include/storage/proc.h -*** base/src/include/storage/proc.h Thu Feb 26 10:18:55 2009 ---- sepgsql/src/include/storage/proc.h Tue Dec 8 14:04:25 2009 -*************** typedef struct PROC_HDR -*** 143,150 **** - * normal operation. Startup process also consumes one slot, but WAL - * writer and autovacuum launcher are launched only after it has - * exited. - */ -! #define NUM_AUXILIARY_PROCS 3 - - - /* configurable options */ ---- 143,152 ---- - * normal operation. Startup process also consumes one slot, but WAL - * writer and autovacuum launcher are launched only after it has - * exited. -+ * In addition, a netlink receiver process may be launched, if SELinux -+ * support is enabled. - */ -! #define NUM_AUXILIARY_PROCS 4 - - - /* configurable options */ -diff -Nrpc base/src/include/utils/errcodes.h sepgsql/src/include/utils/errcodes.h -*** base/src/include/utils/errcodes.h Fri Mar 6 09:45:33 2009 ---- sepgsql/src/include/utils/errcodes.h Wed Jul 15 19:35:52 2009 -*************** -*** 347,352 **** ---- 347,357 ---- - #define ERRCODE_NO_DATA_FOUND MAKE_SQLSTATE('P','0', '0','0','2') - #define ERRCODE_TOO_MANY_ROWS MAKE_SQLSTATE('P','0', '0','0','3') - -+ /* Class SE - SE-PostgreSQL Error */ -+ #define ERRCODE_SELINUX_ERROR MAKE_SQLSTATE('S','E', '0','0','1') -+ #define ERRCODE_SELINUX_AUDIT MAKE_SQLSTATE('S','E', '0','0','2') -+ #define ERRCODE_SELINUX_INFO MAKE_SQLSTATE('S','E', '0','0','3') -+ - /* Class XX - Internal Error (PostgreSQL-specific error class) */ - /* (this is for "can't-happen" conditions and software bugs) */ - #define ERRCODE_INTERNAL_ERROR MAKE_SQLSTATE('X','X', '0','0','0') -diff -Nrpc base/src/include/utils/syscache.h sepgsql/src/include/utils/syscache.h -*** base/src/include/utils/syscache.h Sat Jan 3 12:25:21 2009 ---- sepgsql/src/include/utils/syscache.h Wed Sep 9 21:29:33 2009 -*************** enum SysCacheIdentifier -*** 69,74 **** ---- 69,76 ---- - RELNAMENSP, - RELOID, - RULERELNAME, -+ SECURITYATTR, -+ SECURITYSECID, - STATRELATT, - TSCONFIGMAP, - TSCONFIGNAMENSP, -diff -Nrpc base/src/test/regress/GNUmakefile sepgsql/src/test/regress/GNUmakefile -*** base/src/test/regress/GNUmakefile Sat Jan 3 13:01:35 2009 ---- sepgsql/src/test/regress/GNUmakefile Tue Dec 1 17:11:40 2009 -*************** ifdef NO_LOCALE -*** 38,43 **** ---- 38,49 ---- - NOLOCALE += --no-locale - endif - -+ # SELinux support -+ ENABLE_SELINUX = -+ ifdef SELINUX -+ ENABLE_SELINUX += --enable-selinux -+ endif -+ - # stuff to pass into build of pg_regress - EXTRADEFS = '-DHOST_TUPLE="$(host_tuple)"' \ - '-DMAKEPROG="$(MAKE)"' \ -*************** tablespace-setup: -*** 138,144 **** - ## Run tests - ## - -! pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) - - check: all - $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) ---- 144,150 ---- - ## Run tests - ## - -! pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) $(ENABLE_SELINUX) - - check: all - $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) -diff -Nrpc base/src/test/regress/expected/sanity_check.out sepgsql/src/test/regress/expected/sanity_check.out -*** base/src/test/regress/expected/sanity_check.out Tue Feb 10 10:10:02 2009 ---- sepgsql/src/test/regress/expected/sanity_check.out Thu Jul 16 23:18:11 2009 -*************** SELECT relname, relhasindex -*** 112,117 **** ---- 112,118 ---- - pg_pltemplate | t - pg_proc | t - pg_rewrite | t -+ pg_security | t - pg_shdepend | t - pg_shdescription | t - pg_statistic | t -*************** SELECT relname, relhasindex -*** 151,157 **** - timetz_tbl | f - tinterval_tbl | f - varchar_tbl | f -! (140 rows) - - -- - -- another sanity check: every system catalog that has OIDs should have ---- 152,158 ---- - timetz_tbl | f - tinterval_tbl | f - varchar_tbl | f -! (141 rows) - - -- - -- another sanity check: every system catalog that has OIDs should have -diff -Nrpc base/src/test/regress/pg_regress.c sepgsql/src/test/regress/pg_regress.c -*** base/src/test/regress/pg_regress.c 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 5711591..5fe52de 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -12,7 +12,7 @@ Summary: Security Enhanced PostgreSQL Name: sepostgresql Version: 8.4.2 -Release: 2487%{?dist} +Release: 2488%{?dist} License: BSD Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ @@ -21,8 +21,9 @@ Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.t Source1: sepostgresql.init Source2: sepostgresql.8 Source3: sepostgresql.logrotate -Patch0: sepostgresql-8.4.patch -Patch1: sepostgresql-fedora-prefix.patch +Patch0: sepostgresql-fedora-prefix.patch +Patch1: pgsql-01-8.4-blobs.patch +Patch2: pgsql-02-8.4-sepgsql.patch BuildRequires: perl glibc-devel bison flex readline-devel zlib-devel >= 1.0.4 BuildRequires: checkpolicy libselinux-devel >= 2.0.80 BuildRequires: selinux-policy >= 3.6.8 @@ -50,6 +51,7 @@ reference monitor to check any SQL query. %setup -q -n postgresql-%{version} %patch0 -p1 %patch1 -p1 +%patch2 -p1 %build CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS @@ -192,6 +194,10 @@ fi %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog +* Mon Feb 15 2010 KaiGai Kohei - 8.4.2-2488 +- fix: build failed due to an implicit header file include +- update: feature backport from v8.5 development + * Wed Dec 16 2009 KaiGai Kohei - 8.4.2-2487 - upgrade base version 8.4.1->8.4.2 From e328bd9fdd1d0a2310b7253ab36cd492819a2729 Mon Sep 17 00:00:00 2001 From: Jesse Keating Date: Wed, 17 Feb 2010 03:10:15 +0000 Subject: [PATCH 05/24] Initialize branch F-13 for sepostgresql --- branch | 1 + 1 file changed, 1 insertion(+) create mode 100644 branch diff --git a/branch b/branch new file mode 100644 index 0000000..baa94ef --- /dev/null +++ b/branch @@ -0,0 +1 @@ +F-13 From 9657ec6540425fd26a9ac7f31675e50b3d41e36e Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Thu, 18 Mar 2010 01:05:00 +0000 Subject: [PATCH 06/24] upgrade base pgsql 8.4.2->8.4.3 --- .cvsignore | 1 + pgsql-01-8.4-blobs.patch | 4800 +++-- pgsql-02-8.4-sepgsql.patch | 37849 +++++++++++++++++++---------------- sepostgresql.spec | 7 +- sources | 1 + 5 files changed, 22860 insertions(+), 19798 deletions(-) diff --git a/.cvsignore b/.cvsignore index 227e30f..02d7344 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1,2 @@ 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..6b61e74 100644 --- a/sources +++ b/sources @@ -1 +1,2 @@ d738227e2f1f742d2f2d4ab56496c5c6 postgresql-8.4.2.tar.bz2 +7f70e7b140fb190f268837255582b07e postgresql-8.4.3.tar.bz2 From 62301f6c4e04faaa989010c1b3e0af63757909db Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Thu, 18 Mar 2010 01:11:03 +0000 Subject: [PATCH 07/24] 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 From 9ec59cc87f8097145952fcbc656e2114a60b2413 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Sat, 3 Apr 2010 12:09:31 +0000 Subject: [PATCH 08/24] upgrade base version 8.4.x -> 9.0alpha5 --- .cvsignore | 3 +- pgsql-01-8.4-blobs.patch | 2600 ---- pgsql-02-8.4-sepgsql.patch | 20258 ----------------------------- sepostgresql-9.0-fullset.patch | 19003 +++++++++++++++++++++++++++ sepostgresql-fedora-prefix.patch | 84 +- sepostgresql.init | 29 +- sepostgresql.spec | 68 +- sources | 3 +- 8 files changed, 19098 insertions(+), 22950 deletions(-) delete mode 100644 pgsql-01-8.4-blobs.patch delete mode 100644 pgsql-02-8.4-sepgsql.patch create mode 100644 sepostgresql-9.0-fullset.patch diff --git a/.cvsignore b/.cvsignore index 02d7344..54ba6ee 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1,2 +1 @@ -postgresql-8.4.2.tar.bz2 -postgresql-8.4.3.tar.bz2 +postgresql-9.0alpha5.tar.bz2 diff --git a/pgsql-01-8.4-blobs.patch b/pgsql-01-8.4-blobs.patch deleted file mode 100644 index b2ef970..0000000 --- a/pgsql-01-8.4-blobs.patch +++ /dev/null @@ -1,2600 +0,0 @@ -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)); -+ -+ 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) - { -*************** ExecGrant_Language(InternalGrant *istmt) -*** 1782,1787 **** ---- 1809,1946 ---- - } - - 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; -*************** 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 deleted file mode 100644 index 0339b07..0000000 --- a/pgsql-02-8.4-sepgsql.patch +++ /dev/null @@ -1,20258 +0,0 @@ -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-9.0-fullset.patch b/sepostgresql-9.0-fullset.patch new file mode 100644 index 0000000..37109a4 --- /dev/null +++ b/sepostgresql-9.0-fullset.patch @@ -0,0 +1,19003 @@ +diff --git a/configure b/configure +index 7e34c4f..7bfa5ff 100755 +--- a/configure ++++ b/configure +@@ -707,6 +707,7 @@ LDFLAGS_SL + ELF_SYS + EGREP + GREP ++enable_selinux + with_zlib + with_system_tzdata + with_libxslt +@@ -842,6 +843,7 @@ with_libxml + with_libxslt + with_system_tzdata + with_zlib ++enable_selinux + with_gnu_ld + enable_largefile + enable_float4_byval +@@ -1498,6 +1500,7 @@ Optional Features: + --enable-depend turn on automatic dependency tracking + --enable-cassert enable assertion checks (for debugging) + --disable-thread-safety disable thread-safety in client libraries ++ --enable-selinux build with SELinux support + --disable-largefile omit support for large files + --disable-float4-byval disable float4 passed by value + --disable-float8-byval disable float8 passed by value +@@ -5608,6 +5611,201 @@ fi + + + # ++# SELinux support ++# ++ ++ ++# Check whether --enable-selinux was given. ++if test "${enable_selinux+set}" = set; then ++ enableval=$enable_selinux; ++ case $enableval in ++ yes) ++ : ++ ;; ++ no) ++ : ++ ;; ++ *) ++ { { $as_echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5 ++$as_echo "$as_me: error: no argument expected for --enable-selinux option" >&2;} ++ { (exit 1); exit 1; }; } ++ ;; ++ esac ++ ++else ++ enable_selinux=no ++ ++fi ++ ++ ++if test "$enable_selinux" = yes; then ++ ++{ $as_echo "$as_me:$LINENO: checking for avc_open in -lselinux" >&5 ++$as_echo_n "checking for avc_open in -lselinux... " >&6; } ++if test "${ac_cv_lib_selinux_avc_open+set}" = set; then ++ $as_echo_n "(cached) " >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lselinux $LIBS" ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char avc_open (); ++int ++main () ++{ ++return avc_open (); ++ ; ++ return 0; ++} ++_ACEOF ++rm -f conftest.$ac_objext conftest$ac_exeext ++if { (ac_try="$ac_link" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" ++$as_echo "$ac_try_echo") >&5 ++ (eval "$ac_link") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest$ac_exeext && { ++ test "$cross_compiling" = yes || ++ $as_test_x conftest$ac_exeext ++ }; then ++ ac_cv_lib_selinux_avc_open=yes ++else ++ $as_echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_cv_lib_selinux_avc_open=no ++fi ++ ++rm -rf conftest.dSYM ++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_avc_open" >&5 ++$as_echo "$ac_cv_lib_selinux_avc_open" >&6; } ++if test "x$ac_cv_lib_selinux_avc_open" = x""yes; then ++ cat >>confdefs.h <<_ACEOF ++#define HAVE_LIBSELINUX 1 ++_ACEOF ++ ++ LIBS="-lselinux $LIBS" ++ ++else ++ { { $as_echo "$as_me:$LINENO: error: \"SELinux support requires libselinux.\"" >&5 ++$as_echo "$as_me: error: \"SELinux support requires libselinux.\"" >&2;} ++ { (exit 1); exit 1; }; } ++fi ++ ++ ++{ $as_echo "$as_me:$LINENO: checking for audit_open in -laudit" >&5 ++$as_echo_n "checking for audit_open in -laudit... " >&6; } ++if test "${ac_cv_lib_audit_audit_open+set}" = set; then ++ $as_echo_n "(cached) " >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-laudit $LIBS" ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char audit_open (); ++int ++main () ++{ ++return audit_open (); ++ ; ++ return 0; ++} ++_ACEOF ++rm -f conftest.$ac_objext conftest$ac_exeext ++if { (ac_try="$ac_link" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" ++$as_echo "$ac_try_echo") >&5 ++ (eval "$ac_link") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest$ac_exeext && { ++ test "$cross_compiling" = yes || ++ $as_test_x conftest$ac_exeext ++ }; then ++ ac_cv_lib_audit_audit_open=yes ++else ++ $as_echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_cv_lib_audit_audit_open=no ++fi ++ ++rm -rf conftest.dSYM ++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_audit_audit_open" >&5 ++$as_echo "$ac_cv_lib_audit_audit_open" >&6; } ++if test "x$ac_cv_lib_audit_audit_open" = x""yes; then ++ cat >>confdefs.h <<_ACEOF ++#define HAVE_LIBAUDIT 1 ++_ACEOF ++ ++ LIBS="-laudit $LIBS" ++ ++else ++ { { $as_echo "$as_me:$LINENO: error: \"SELinux support requires libaudit.\"" >&5 ++$as_echo "$as_me: error: \"SELinux support requires libaudit.\"" >&2;} ++ { (exit 1); exit 1; }; } ++fi ++ ++ ++cat >>confdefs.h <<_ACEOF ++#define HAVE_SELINUX 1 ++_ACEOF ++ ++ ++fi ++ ++# + # Elf + # + +diff --git a/configure.in b/configure.in +index bbeea97..e499e10 100644 +--- a/configure.in ++++ b/configure.in +@@ -755,6 +755,19 @@ PGAC_ARG_BOOL(with, zlib, yes, + AC_SUBST(with_zlib) + + # ++# SELinux support ++# ++PGAC_ARG_BOOL(enable, selinux, no, [build with SELinux support]) ++if test "$enable_selinux" = yes; then ++ AC_CHECK_LIB(selinux, avc_open,, ++ AC_MSG_ERROR("SELinux support requires libselinux.")) ++ AC_CHECK_LIB(audit, audit_open,, ++ AC_MSG_ERROR("SELinux support requires libaudit.")) ++ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, [SE-PostgreSQL feature is enabled]) ++ AC_SUBST(enable_selinux) ++fi ++ ++# + # Elf + # + +diff --git a/src/Makefile.global.in b/src/Makefile.global.in +index 1c38ac2..0e3bed5 100644 +--- a/src/Makefile.global.in ++++ b/src/Makefile.global.in +@@ -164,6 +164,7 @@ enable_nls = @enable_nls@ + enable_debug = @enable_debug@ + enable_dtrace = @enable_dtrace@ + enable_coverage = @enable_coverage@ ++enable_selinux = @enable_selinux@ + enable_thread_safety = @enable_thread_safety@ + + python_includespec = @python_includespec@ +diff --git a/src/backend/Makefile b/src/backend/Makefile +index 218544e..0b1dd31 100644 +--- a/src/backend/Makefile ++++ b/src/backend/Makefile +@@ -16,7 +16,7 @@ include $(top_builddir)/src/Makefile.global + + SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ + main nodes optimizer port postmaster regex replication rewrite \ +- storage tcop tsearch utils $(top_builddir)/src/timezone ++ sepgsql storage tcop tsearch utils $(top_builddir)/src/timezone + + include $(srcdir)/common.mk + +@@ -40,6 +40,11 @@ LIBS := $(filter-out -lpgport, $(LIBS)) $(LDAP_LIBS_BE) + # The backend doesn't need everything that's in LIBS, however + LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) + ++# SELinux needs libselinux and libaudit ++ifeq ($(enable_selinux), yes) ++LIBS := $(filter-out -lselinux -laudit, $(LIBS)) -lselinux -laudit ++endif ++ + ########################################################################## + + all: submake-libpgport submake-schemapg postgres $(POSTGRES_IMP) +diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c +index 6ec73f0..db0023f 100644 +--- a/src/backend/access/common/heaptuple.c ++++ b/src/backend/access/common/heaptuple.c +@@ -60,6 +60,7 @@ + #include "access/heapam.h" + #include "access/sysattr.h" + #include "access/tuptoaster.h" ++#include "catalog/pg_seclabel.h" + #include "executor/tuptable.h" + + +@@ -559,6 +560,9 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) + case TableOidAttributeNumber: + result = ObjectIdGetDatum(tup->t_tableOid); + break; ++ case SecurityLabelAttributeNumber: ++ result = seclabelSysattOutput(tup->t_tableOid, tup); ++ break; + default: + elog(ERROR, "invalid attnum: %d", attnum); + result = 0; /* keep compiler quiet */ +@@ -682,6 +686,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, + + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); ++ if (tupleDescriptor->tdhassecid) ++ len += sizeof(Oid)+1; + + hoff = len = MAXALIGN(len); /* align user data safely */ + +@@ -713,6 +719,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, + + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + td->t_infomask = HEAP_HASOID; ++ if (tupleDescriptor->tdhassecid) ++ td->t_infomask |= HEAP_HASSECID; + + heap_fill_tuple(tupleDescriptor, + values, +@@ -824,6 +832,8 @@ heap_modify_tuple(HeapTuple tuple, + newTuple->t_tableOid = tuple->t_tableOid; + if (tupleDesc->tdhasoid) + HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); ++ if (tupleDesc->tdhassecid) ++ HeapTupleSetSecid(newTuple, HeapTupleGetSecid(tuple)); + + return newTuple; + } +@@ -1434,6 +1444,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor, + + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); ++ if (tupleDescriptor->tdhassecid) ++ len += sizeof(Oid); + + hoff = len = MAXALIGN(len); /* align user data safely */ + +@@ -1455,6 +1467,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor, + + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + tuple->t_infomask = HEAP_HASOID; ++ if (tupleDescriptor->tdhassecid) ++ tuple->t_infomask |= HEAP_HASSECID; + + heap_fill_tuple(tupleDescriptor, + values, +diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c +index 9a8611f..325dbde 100644 +--- a/src/backend/access/common/tupdesc.c ++++ b/src/backend/access/common/tupdesc.c +@@ -34,7 +34,7 @@ + * caller can overwrite this if needed. + */ + TupleDesc +-CreateTemplateTupleDesc(int natts, bool hasoid) ++CreateTemplateTupleDesc(int natts, bool hasoid, bool hassecid) + { + TupleDesc desc; + char *stg; +@@ -88,6 +88,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid) + desc->tdtypeid = RECORDOID; + desc->tdtypmod = -1; + desc->tdhasoid = hasoid; ++ desc->tdhassecid = hassecid; + desc->tdrefcount = -1; /* assume not reference-counted */ + + return desc; +@@ -105,7 +106,8 @@ CreateTemplateTupleDesc(int natts, bool hasoid) + * caller can overwrite this if needed. + */ + TupleDesc +-CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs) ++CreateTupleDesc(int natts, bool hasoid, bool hassecid, ++ Form_pg_attribute *attrs) + { + TupleDesc desc; + +@@ -121,6 +123,7 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs) + desc->tdtypeid = RECORDOID; + desc->tdtypmod = -1; + desc->tdhasoid = hasoid; ++ desc->tdhassecid = hassecid; + desc->tdrefcount = -1; /* assume not reference-counted */ + + return desc; +@@ -139,7 +142,8 @@ CreateTupleDescCopy(TupleDesc tupdesc) + TupleDesc desc; + int i; + +- desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid); ++ desc = CreateTemplateTupleDesc(tupdesc->natts, ++ tupdesc->tdhasoid, tupdesc->tdhassecid); + + for (i = 0; i < desc->natts; i++) + { +@@ -150,6 +154,7 @@ CreateTupleDescCopy(TupleDesc tupdesc) + + desc->tdtypeid = tupdesc->tdtypeid; + desc->tdtypmod = tupdesc->tdtypmod; ++ desc->tdhassecid = tupdesc->tdhassecid; + + return desc; + } +@@ -166,7 +171,8 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) + TupleConstr *constr = tupdesc->constr; + int i; + +- desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid); ++ desc = CreateTemplateTupleDesc(tupdesc->natts, ++ tupdesc->tdhasoid, tupdesc->tdhassecid); + + for (i = 0; i < desc->natts; i++) + { +@@ -314,6 +320,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) + return false; + if (tupdesc1->tdhasoid != tupdesc2->tdhasoid) + return false; ++ if (tupdesc1->tdhassecid != tupdesc2->tdhassecid) ++ return false; + + for (i = 0; i < tupdesc1->natts; i++) + { +@@ -519,7 +527,7 @@ BuildDescForRelation(List *schema) + * allocate a new tuple descriptor + */ + natts = list_length(schema); +- desc = CreateTemplateTupleDesc(natts, false); ++ desc = CreateTemplateTupleDesc(natts, false, false); + has_not_null = false; + + attnum = 0; +@@ -604,7 +612,7 @@ BuildDescFromLists(List *names, List *types, List *typmods) + /* + * allocate a new tuple descriptor + */ +- desc = CreateTemplateTupleDesc(natts, false); ++ desc = CreateTemplateTupleDesc(natts, false, false); + + attnum = 0; + +diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c +index f01ed1e..4e8dfcb 100644 +--- a/src/backend/access/gin/ginutil.c ++++ b/src/backend/access/gin/ginutil.c +@@ -33,7 +33,7 @@ initGinState(GinState *state, Relation index) + + for (i = 0; i < index->rd_att->natts; i++) + { +- state->tupdesc[i] = CreateTemplateTupleDesc(2, false); ++ state->tupdesc[i] = CreateTemplateTupleDesc(2, false, false); + + TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL, + INT2OID, -1, 0); +diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c +index bb57cb9..d17274f 100644 +--- a/src/backend/access/heap/heapam.c ++++ b/src/backend/access/heap/heapam.c +@@ -52,6 +52,7 @@ + #include "access/xlogutils.h" + #include "catalog/catalog.h" + #include "catalog/namespace.h" ++#include "catalog/pg_seclabel.h" + #include "miscadmin.h" + #include "pgstat.h" + #include "storage/bufmgr.h" +@@ -1862,6 +1863,22 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid, + Assert(!(tup->t_data->t_infomask & HEAP_HASOID)); + } + ++ /* ++ * If this tuple has a capability to store its security id, but it has ++ * not been assigned yet, the default security id should be assigned. ++ * Note that this step does not apply any permission checks. All the ++ * caller of heap_insert() is trusted. ++ */ ++ if (relation->rd_rel->relhassecids) ++ { ++ if (!OidIsValid(HeapTupleGetSecid(tup))) ++ HeapTupleSetSecid(tup, seclabelGetNewSecid(relation, tup)); ++ } ++ else ++ { ++ Assert(!HeapTupleHasSecid(tup)); ++ } ++ + tup->t_data->t_infomask &= ~(HEAP_XACT_MASK); + tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); + tup->t_data->t_infomask |= HEAP_XMAX_INVALID; +@@ -2560,6 +2577,20 @@ l2: + Assert(!(newtup->t_data->t_infomask & HEAP_HASOID)); + } + ++ /* ++ * Preserve security-id, if not changed ++ */ ++ if (relation->rd_rel->relhassecids) ++ { ++ if (!OidIsValid(HeapTupleGetSecid(newtup))) ++ HeapTupleSetSecid(newtup, HeapTupleGetSecid(&oldtup)); ++ } ++ else ++ { ++ /* check there is not space for a security-id */ ++ Assert(!HeapTupleHasSecid(newtup)); ++ } ++ + newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK); + newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); + newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED); +@@ -3501,6 +3532,10 @@ heap_inplace_update(Relation relation, HeapTuple tuple) + memcpy((char *) htup + htup->t_hoff, + (char *) tuple->t_data + tuple->t_data->t_hoff, + newlen); ++ if (HeapTupleHeaderHasOid(htup)) ++ HeapTupleHeaderSetOid(htup, HeapTupleGetOid(tuple)); ++ if (HeapTupleHeaderHasSecid(htup)) ++ HeapTupleHeaderSetSecid(htup, HeapTupleGetSecid(tuple)); + + MarkBufferDirty(buffer); + +diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c +index 2af81df..18341f4 100644 +--- a/src/backend/access/heap/tuptoaster.c ++++ b/src/backend/access/heap/tuptoaster.c +@@ -591,6 +591,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, + hoff += BITMAPLEN(numAttrs); + if (newtup->t_data->t_infomask & HEAP_HASOID) + hoff += sizeof(Oid); ++ if (HeapTupleHasSecid(newtup)) ++ hoff += sizeof(Oid); + hoff = MAXALIGN(hoff); + Assert(hoff == newtup->t_data->t_hoff); + /* now convert to a limit on the tuple data size */ +@@ -868,6 +870,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, + new_len += BITMAPLEN(numAttrs); + if (olddata->t_infomask & HEAP_HASOID) + new_len += sizeof(Oid); ++ if (HeapTupleHeaderHasSecid(olddata)) ++ new_len += sizeof(Oid); + new_len = MAXALIGN(new_len); + Assert(new_len == olddata->t_hoff); + new_data_len = heap_compute_data_size(tupleDesc, +@@ -1019,6 +1023,8 @@ toast_flatten_tuple_attribute(Datum value, + new_len += BITMAPLEN(numAttrs); + if (olddata->t_infomask & HEAP_HASOID) + new_len += sizeof(Oid); ++ if (HeapTupleHeaderHasSecid(olddata)) ++ new_len += sizeof(Oid); + new_len = MAXALIGN(new_len); + Assert(new_len == olddata->t_hoff); + new_data_len = heap_compute_data_size(tupleDesc, +diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c +index e2566a4..c21054b 100644 +--- a/src/backend/access/transam/twophase.c ++++ b/src/backend/access/transam/twophase.c +@@ -605,7 +605,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS) + + /* build tupdesc for result tuples */ + /* this had better match pg_prepared_xacts view in system_views.sql */ +- tupdesc = CreateTemplateTupleDesc(5, false); ++ tupdesc = CreateTemplateTupleDesc(5, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "transaction", + XIDOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "gid", +diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c +index b88cff2..d6636f7 100644 +--- a/src/backend/access/transam/xact.c ++++ b/src/backend/access/transam/xact.c +@@ -36,6 +36,7 @@ + #include "libpq/be-fsstubs.h" + #include "miscadmin.h" + #include "pgstat.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "storage/lmgr.h" +@@ -140,6 +141,8 @@ typedef struct TransactionStateData + int maxChildXids; /* allocated size of childXids[] */ + Oid prevUser; /* previous CurrentUserId setting */ + int prevSecContext; /* previous SecurityRestrictionContext */ ++ char *prevSecLabel; /* previous security label of client */ ++ int prevRowlvMode; /* previous row-level access control mode */ + bool prevXactReadOnly; /* entry-time xact r/o state */ + bool startedInRecovery; /* did we start in recovery? */ + struct TransactionStateData *parent; /* back link to parent */ +@@ -169,6 +172,8 @@ static TransactionStateData TopTransactionStateData = { + 0, /* allocated size of childXids[] */ + InvalidOid, /* previous CurrentUserId setting */ + 0, /* previous SecurityRestrictionContext */ ++ NULL, /* previous security label of the client */ ++ 0, /* previous row-level access control mode */ + false, /* entry-time xact r/o state */ + false, /* startedInRecovery */ + NULL /* link to parent state block */ +@@ -1658,6 +1663,10 @@ StartTransaction(void) + /* SecurityRestrictionContext should never be set outside a transaction */ + Assert(s->prevSecContext == 0); + ++ /* Save status of SELinux */ ++ s->prevSecLabel = sepgsql_get_client_label(); ++ s->prevRowlvMode = sepgsql_rowlv_get_mode(); ++ + /* + * initialize other subsystems for new transaction + */ +@@ -2162,6 +2171,10 @@ AbortTransaction(void) + */ + SetUserIdAndSecContext(s->prevUser, s->prevSecContext); + ++ /* Reset SELinux status */ ++ sepgsql_set_client_label(s->prevSecLabel); ++ sepgsql_rowlv_set_mode(s->prevRowlvMode); ++ + /* + * do abort processing + */ +@@ -4006,6 +4019,10 @@ AbortSubTransaction(void) + */ + SetUserIdAndSecContext(s->prevUser, s->prevSecContext); + ++ /* Reset SELinux status */ ++ sepgsql_set_client_label(s->prevSecLabel); ++ sepgsql_rowlv_set_mode(s->prevRowlvMode); ++ + /* + * We can skip all this stuff if the subxact failed before creating a + * ResourceOwner... +@@ -4145,6 +4162,8 @@ PushTransaction(void) + s->state = TRANS_DEFAULT; + s->blockState = TBLOCK_SUBBEGIN; + GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); ++ s->prevSecLabel = sepgsql_get_client_label(); ++ s->prevRowlvMode = sepgsql_rowlv_get_mode(); + s->prevXactReadOnly = XactReadOnly; + + CurrentTransactionState = s; +diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c +index abdf4d8..1b5b84c 100644 +--- a/src/backend/access/transam/xlog.c ++++ b/src/backend/access/transam/xlog.c +@@ -8427,7 +8427,7 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS) + * Construct a tuple descriptor for the result row. This must match this + * function's pg_proc entry! + */ +- resultTupleDesc = CreateTemplateTupleDesc(2, false); ++ resultTupleDesc = CreateTemplateTupleDesc(2, false, false); + TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "file_name", + TEXTOID, -1, 0); + TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "file_offset", +diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y +index 387d43e..aecb449 100644 +--- a/src/backend/bootstrap/bootparse.y ++++ b/src/backend/bootstrap/bootparse.y +@@ -32,6 +32,7 @@ + #include "catalog/pg_authid.h" + #include "catalog/pg_class.h" + #include "catalog/pg_namespace.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_tablespace.h" + #include "catalog/toasting.h" + #include "commands/defrem.h" +@@ -187,10 +188,11 @@ Boot_CreateStmt: + TupleDesc tupdesc; + bool shared_relation; + bool mapped_relation; ++ bool hassecid = seclabelCatalogHasSysAttr($3); + + do_start(); + +- tupdesc = CreateTupleDesc(numattr, !($6), attrtypes); ++ tupdesc = CreateTupleDesc(numattr, !($6), hassecid, attrtypes); + + shared_relation = $5; + +@@ -245,7 +247,8 @@ Boot_CreateStmt: + ONCOMMIT_NOOP, + (Datum) 0, + false, +- true); ++ true, ++ NULL); + elog(DEBUG4, "relation created with oid %u", id); + } + do_end(); +diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c +index 46e8bae..5f8c824 100644 +--- a/src/backend/bootstrap/bootstrap.c ++++ b/src/backend/bootstrap/bootstrap.c +@@ -33,6 +33,7 @@ + #include "postmaster/bgwriter.h" + #include "postmaster/walwriter.h" + #include "replication/walreceiver.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/ipc.h" + #include "storage/proc.h" +@@ -319,6 +320,9 @@ AuxiliaryProcessMain(int argc, char *argv[]) + case WalReceiverProcess: + statmsg = "wal receiver process"; + break; ++ case SecurityWorkerProcess: ++ statmsg = "security worker process"; ++ break; + default: + statmsg = "??? process"; + break; +@@ -429,6 +433,10 @@ AuxiliaryProcessMain(int argc, char *argv[]) + WalReceiverMain(); + proc_exit(1); /* should never return */ + ++ case SecurityWorkerProcess: ++ sepgsql_worker_main(); ++ proc_exit(1); /* should never return */ ++ + default: + elog(PANIC, "unrecognized process type: %d", auxType); + proc_exit(1); +@@ -493,6 +501,11 @@ BootstrapModeMain(void) + boot_yyparse(); + + /* ++ * Initial security labeling ++ */ ++ sepgsql_post_bootstraping(); ++ ++ /* + * We should now know about all mapped relations, so it's okay to write + * out the initial relation mapping files. + */ +@@ -794,6 +807,7 @@ InsertOneTuple(Oid objectid) + + tupDesc = CreateTupleDesc(numattr, + RelationGetForm(boot_reldesc)->relhasoids, ++ RelationGetForm(boot_reldesc)->relhassecids, + attrtypes); + tuple = heap_form_tuple(tupDesc, values, Nulls); + if (objectid != (Oid) 0) +diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile +index dafae3f..c2aea7a 100644 +--- a/src/backend/catalog/Makefile ++++ b/src/backend/catalog/Makefile +@@ -13,7 +13,7 @@ include $(top_builddir)/src/Makefile.global + OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ + pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ + pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ +- pg_db_role_setting.o pg_shdepend.o pg_type.o storage.o toasting.o ++ pg_db_role_setting.o pg_seclabel.o pg_shdepend.o pg_type.o storage.o toasting.o + + BKIFILES = postgres.bki postgres.description postgres.shdescription + +@@ -34,7 +34,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ + pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ + pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \ + pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ +- pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ ++ pg_seclabel.h pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ + pg_ts_parser.h pg_ts_template.h \ + pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ + pg_default_acl.h \ +diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c +index d16b03b..a9d5184 100644 +--- a/src/backend/catalog/aclchk.c ++++ b/src/backend/catalog/aclchk.c +@@ -38,6 +38,7 @@ + #include "catalog/pg_operator.h" + #include "catalog/pg_opfamily.h" + #include "catalog/pg_proc.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_type.h" + #include "catalog/pg_ts_config.h" +@@ -46,6 +47,7 @@ + #include "foreign/foreign.h" + #include "miscadmin.h" + #include "parser/parse_func.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" +@@ -1460,6 +1462,10 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, + if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) + continue; + ++ /* Skip security label column, if it doesn't exist */ ++ if (curr_att == SecurityLabelAttributeNumber && !classForm->relhassecids) ++ continue; ++ + /* Views don't have any system columns at all */ + if (classForm->relkind == RELKIND_VIEW && curr_att < 0) + continue; +@@ -1560,6 +1566,8 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, + relOid, grantorId, ACL_KIND_COLUMN, + relname, attnum, + NameStr(pg_attribute_tuple->attname)); ++ /* SELinux checks */ ++ sepgsql_attribute_grant(relOid, attnum); + + /* + * Generate new ACL. +@@ -1813,6 +1821,8 @@ ExecGrant_Relation(InternalGrant *istmt) + ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, + NameStr(pg_class_tuple->relname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_relation_grant(relOid); + + /* + * Generate new ACL. +@@ -1999,6 +2009,8 @@ ExecGrant_Database(InternalGrant *istmt) + datId, grantorId, ACL_KIND_DATABASE, + NameStr(pg_database_tuple->datname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_database_grant(datId); + + /* + * Generate new ACL. +@@ -2116,6 +2128,8 @@ ExecGrant_Fdw(InternalGrant *istmt) + fdwid, grantorId, ACL_KIND_FDW, + NameStr(pg_fdw_tuple->fdwname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_fdw_grant(fdwid); + + /* + * Generate new ACL. +@@ -2233,6 +2247,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) + srvid, grantorId, ACL_KIND_FOREIGN_SERVER, + NameStr(pg_server_tuple->srvname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_fserver_grant(srvid); + + /* + * Generate new ACL. +@@ -2719,6 +2735,8 @@ ExecGrant_Namespace(InternalGrant *istmt) + nspid, grantorId, ACL_KIND_NAMESPACE, + NameStr(pg_namespace_tuple->nspname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_schema_grant(nspid); + + /* + * Generate new ACL. +@@ -2835,6 +2853,8 @@ ExecGrant_Tablespace(InternalGrant *istmt) + tblId, grantorId, ACL_KIND_TABLESPACE, + NameStr(pg_tablespace_tuple->spcname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_tablespace_grant(tblId); + + /* + * Generate new ACL. +diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c +index 7efdf67..9376f6c 100644 +--- a/src/backend/catalog/catalog.c ++++ b/src/backend/catalog/catalog.c +@@ -32,6 +32,7 @@ + #include "catalog/pg_namespace.h" + #include "catalog/pg_pltemplate.h" + #include "catalog/pg_db_role_setting.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_shdepend.h" + #include "catalog/pg_shdescription.h" + #include "catalog/pg_tablespace.h" +@@ -306,6 +307,7 @@ IsSharedRelation(Oid relationId) + relationId == AuthMemRelationId || + relationId == DatabaseRelationId || + relationId == PLTemplateRelationId || ++ relationId == SecLabelRelationId || + relationId == SharedDescriptionRelationId || + relationId == SharedDependRelationId || + relationId == TableSpaceRelationId || +@@ -319,6 +321,8 @@ IsSharedRelation(Oid relationId) + relationId == DatabaseNameIndexId || + relationId == DatabaseOidIndexId || + relationId == PLTemplateNameIndexId || ++ relationId == SecLabelSecidIndexId || ++ relationId == SecLabelLabelIndexId || + relationId == SharedDescriptionObjIndexId || + relationId == SharedDependDependerIndexId || + relationId == SharedDependReferenceIndexId || +@@ -331,6 +335,8 @@ IsSharedRelation(Oid relationId) + relationId == PgAuthidToastIndex || + relationId == PgDatabaseToastTable || + relationId == PgDatabaseToastIndex || ++ relationId == PgSecLabelToastTable || ++ relationId == PgSecLabelToastIndex || + relationId == PgShdescriptionToastTable || + relationId == PgShdescriptionToastIndex || + relationId == PgDbRoleSettingToastTable || +diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl +index 9eb805d..30a0779 100644 +--- a/src/backend/catalog/genbki.pl ++++ b/src/backend/catalog/genbki.pl +@@ -216,7 +216,8 @@ foreach my $catname ( @{ $catalogs->{names} } ) + {cmin => 'cid'}, + {xmax => 'xid'}, + {cmax => 'cid'}, +- {tableoid => 'oid'} ++ {tableoid => 'oid'}, ++ {security_label => 'text'} + ); + foreach my $attr (@SYS_ATTRS) + { +diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c +index ec89e49..35bc618 100644 +--- a/src/backend/catalog/heap.c ++++ b/src/backend/catalog/heap.c +@@ -43,6 +43,7 @@ + #include "catalog/pg_constraint.h" + #include "catalog/pg_inherits.h" + #include "catalog/pg_namespace.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_statistic.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_type.h" +@@ -82,14 +83,16 @@ static void AddNewRelationTuple(Relation pg_class_desc, + Oid relowner, + char relkind, + Datum relacl, +- Datum reloptions); ++ Datum reloptions, ++ Oid *secLabels); + static Oid AddNewRelationType(const char *typeName, + Oid typeNamespace, + Oid new_rel_oid, + char new_rel_kind, + Oid ownerid, + Oid new_row_type, +- Oid new_array_type); ++ Oid new_array_type, ++ Oid securityId); + static void RelationRemoveInheritance(Oid relid); + static void StoreRelCheck(Relation rel, char *ccname, Node *expr, + bool is_local, int inhcount); +@@ -173,7 +176,16 @@ static FormData_pg_attribute a7 = { + true, 'p', 'i', true, false, false, true, 0 + }; + +-static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; ++/* ++ * Security Label system column ++ */ ++static FormData_pg_attribute a8 = { ++ 0, {"security_label"}, TEXTOID, 0, -1, ++ SecurityLabelAttributeNumber, 0, -1, -1, ++ false, 'x', 'i', true, false, false, true, 0 ++}; ++ ++static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8}; + + /* + * This function returns a Form_pg_attribute pointer for a system attribute. +@@ -181,12 +193,14 @@ static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; + * happen if there's a problem upstream. + */ + Form_pg_attribute +-SystemAttributeDefinition(AttrNumber attno, bool relhasoids) ++SystemAttributeDefinition(AttrNumber attno, bool relhasoids, bool relhassecids) + { + if (attno >= 0 || attno < -(int) lengthof(SysAtt)) + elog(ERROR, "invalid system attribute number %d", attno); + if (attno == ObjectIdAttributeNumber && !relhasoids) + elog(ERROR, "invalid system attribute number %d", attno); ++ if (attno == SecurityLabelAttributeNumber && !relhassecids) ++ elog(ERROR, "invalid system attribute number %d", attno); + return SysAtt[-attno - 1]; + } + +@@ -195,7 +209,7 @@ SystemAttributeDefinition(AttrNumber attno, bool relhasoids) + * pointer for a prototype definition. If not, return NULL. + */ + Form_pg_attribute +-SystemAttributeByName(const char *attname, bool relhasoids) ++SystemAttributeByName(const char *attname, bool relhasoids, bool relhassecids) + { + int j; + +@@ -203,16 +217,29 @@ SystemAttributeByName(const char *attname, bool relhasoids) + { + Form_pg_attribute att = SysAtt[j]; + +- if (relhasoids || att->attnum != ObjectIdAttributeNumber) +- { +- if (strcmp(NameStr(att->attname), attname) == 0) +- return att; +- } ++ if (!relhasoids && att->attnum == ObjectIdAttributeNumber) ++ continue; ++ if (!relhassecids && att->attnum == SecurityLabelAttributeNumber) ++ continue; ++ ++ if (strcmp(NameStr(att->attname), attname) == 0) ++ return att; + } + + return NULL; + } + ++/* ++ * If the given attribute is writable system attribute, it returns true. ++ */ ++bool ++SystemAttributeWritable(AttrNumber attnum, bool hasoids, bool hassecids) ++{ ++ if (hassecids && attnum == SecurityLabelAttributeNumber) ++ return true; ++ ++ return false; ++} + + /* ---------------------------------------------------------------- + * XXX END OF UGLY HARD CODED BADNESS XXX +@@ -391,7 +418,8 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind, + for (i = 0; i < natts; i++) + { + if (SystemAttributeByName(NameStr(tupdesc->attrs[i]->attname), +- tupdesc->tdhasoid) != NULL) ++ tupdesc->tdhasoid, ++ tupdesc->tdhassecid) != NULL) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_COLUMN), + errmsg("column name \"%s\" conflicts with a system column name", +@@ -509,7 +537,8 @@ CheckAttributeType(const char *attname, Oid atttypid, + void + InsertPgAttributeTuple(Relation pg_attribute_rel, + Form_pg_attribute new_attribute, +- CatalogIndexState indstate) ++ CatalogIndexState indstate, ++ Oid securityId) + { + Datum values[Natts_pg_attribute]; + bool nulls[Natts_pg_attribute]; +@@ -542,6 +571,8 @@ InsertPgAttributeTuple(Relation pg_attribute_rel, + nulls[Anum_pg_attribute_attoptions - 1] = true; + + tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, securityId); + + /* finally insert the new tuple, update the indexes, and clean up */ + simple_heap_insert(pg_attribute_rel, tup); +@@ -566,13 +597,15 @@ AddNewAttributeTuples(Oid new_rel_oid, + TupleDesc tupdesc, + char relkind, + bool oidislocal, +- int oidinhcount) ++ int oidinhcount, ++ Oid *secLabels) + { + Form_pg_attribute attr; + int i; + Relation rel; + CatalogIndexState indstate; + int natts = tupdesc->natts; ++ Oid secid; + ObjectAddress myself, + referenced; + +@@ -596,7 +629,10 @@ AddNewAttributeTuples(Oid new_rel_oid, + attr->attstattarget = -1; + attr->attcacheoff = -1; + +- InsertPgAttributeTuple(rel, attr, indstate); ++ secid = (!secLabels ? InvalidOid : ++ secLabels[i - FirstLowInvalidHeapAttributeNumber]); ++ ++ InsertPgAttributeTuple(rel, attr, indstate, secid); + + /* Add dependency info */ + myself.classId = RelationRelationId; +@@ -624,6 +660,11 @@ AddNewAttributeTuples(Oid new_rel_oid, + SysAtt[i]->attnum == ObjectIdAttributeNumber) + continue; + ++ /* skip security label where appropriate */ ++ if (!tupdesc->tdhassecid && ++ SysAtt[i]->attnum == SecurityLabelAttributeNumber) ++ continue; ++ + memcpy(&attStruct, (char *) SysAtt[i], sizeof(FormData_pg_attribute)); + + /* Fill in the correct relation OID in the copied tuple */ +@@ -636,7 +677,10 @@ AddNewAttributeTuples(Oid new_rel_oid, + attStruct.attinhcount = oidinhcount; + } + +- InsertPgAttributeTuple(rel, &attStruct, indstate); ++ secid = (!secLabels ? InvalidOid ++ : secLabels[SysAtt[i]->attnum - FirstLowInvalidHeapAttributeNumber]); ++ ++ InsertPgAttributeTuple(rel, &attStruct, indstate, secid); + } + } + +@@ -666,7 +710,8 @@ InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, + Datum relacl, +- Datum reloptions) ++ Datum reloptions, ++ Oid securityId) + { + Form_pg_class rd_rel = new_rel_desc->rd_rel; + Datum values[Natts_pg_class]; +@@ -696,6 +741,7 @@ InsertPgClassTuple(Relation pg_class_desc, + values[Anum_pg_class_relnatts - 1] = Int16GetDatum(rd_rel->relnatts); + values[Anum_pg_class_relchecks - 1] = Int16GetDatum(rd_rel->relchecks); + values[Anum_pg_class_relhasoids - 1] = BoolGetDatum(rd_rel->relhasoids); ++ values[Anum_pg_class_relhassecids - 1] = BoolGetDatum(rd_rel->relhassecids); + values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey); + values[Anum_pg_class_relhasexclusion - 1] = BoolGetDatum(rd_rel->relhasexclusion); + values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules); +@@ -719,6 +765,9 @@ InsertPgClassTuple(Relation pg_class_desc, + */ + HeapTupleSetOid(tup, new_rel_oid); + ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, securityId); ++ + /* finally insert the new tuple, update the indexes, and clean up */ + simple_heap_insert(pg_class_desc, tup); + +@@ -743,9 +792,11 @@ AddNewRelationTuple(Relation pg_class_desc, + Oid relowner, + char relkind, + Datum relacl, +- Datum reloptions) ++ Datum reloptions, ++ Oid *secLabels) + { + Form_pg_class new_rel_reltup; ++ Oid secid; + + /* + * first we update some of the information in our uncataloged relation's +@@ -803,9 +854,11 @@ AddNewRelationTuple(Relation pg_class_desc, + + new_rel_desc->rd_att->tdtypeid = new_type_oid; + ++ secid = (!secLabels ? InvalidOid : secLabels[0]); ++ + /* Now build and insert the tuple */ + InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, +- relacl, reloptions); ++ relacl, reloptions, secid); + } + + +@@ -822,7 +875,8 @@ AddNewRelationType(const char *typeName, + char new_rel_kind, + Oid ownerid, + Oid new_row_type, +- Oid new_array_type) ++ Oid new_array_type, ++ Oid securityId) + { + return + TypeCreate(new_row_type, /* optional predetermined OID */ +@@ -854,7 +908,8 @@ AddNewRelationType(const char *typeName, + 'x', /* fully TOASTable */ + -1, /* typmod */ + 0, /* array dimensions for typBaseType */ +- false); /* Type NOT NULL */ ++ false, /* Type NOT NULL */ ++ securityId); /* security-id of the type */ + } + + /* -------------------------------- +@@ -903,7 +958,8 @@ heap_create_with_catalog(const char *relname, + OnCommitAction oncommit, + Datum reloptions, + bool use_user_acl, +- bool allow_system_table_mods) ++ bool allow_system_table_mods, ++ Oid *secLabels) + { + Relation pg_class_desc; + Relation new_rel_desc; +@@ -911,6 +967,7 @@ heap_create_with_catalog(const char *relname, + Oid old_type_oid; + Oid new_type_oid; + Oid new_array_oid = InvalidOid; ++ Oid type_secid = InvalidOid; + + pg_class_desc = heap_open(RelationRelationId, RowExclusiveLock); + +@@ -1030,6 +1087,11 @@ heap_create_with_catalog(const char *relname, + relkind == RELKIND_COMPOSITE_TYPE)) + new_array_oid = AssignTypeArrayOid(); + ++ /* security context of the relation type */ ++ if (secLabels) ++ type_secid = seclabelMoveSecid(TypeRelationId, ++ RelationRelationId, secLabels[0]); ++ + /* + * Since defining a relation also defines a complex type, we add a new + * system type corresponding to the new relation. The OID of the type can +@@ -1046,7 +1108,8 @@ heap_create_with_catalog(const char *relname, + relkind, + ownerid, + reltypeid, +- new_array_oid); ++ new_array_oid, ++ type_secid); + + /* + * Now make the array type if wanted. +@@ -1086,7 +1149,8 @@ heap_create_with_catalog(const char *relname, + 'x', /* fully TOASTable */ + -1, /* typmod */ + 0, /* array dimensions for typBaseType */ +- false); /* Type NOT NULL */ ++ false, /* Type NOT NULL */ ++ type_secid); /* security-id of the type */ + + pfree(relarrayname); + } +@@ -1106,13 +1170,14 @@ heap_create_with_catalog(const char *relname, + ownerid, + relkind, + PointerGetDatum(relacl), +- reloptions); ++ reloptions, ++ secLabels); + + /* + * now add tuples to pg_attribute for the attributes in our new relation. + */ + AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, +- oidislocal, oidinhcount); ++ oidislocal, oidinhcount, secLabels); + + /* + * Make a dependency link to force the relation to be deleted if its +@@ -1590,6 +1655,11 @@ heap_drop_with_catalog(Oid relid) + * delete relation tuple + */ + DeleteRelationTuple(relid); ++ ++ /* ++ * delete orphan pg_seclabel entries ++ */ ++ seclabelOnDropTable(relid); + } + + +diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c +index 69946fe..23fb19b 100644 +--- a/src/backend/catalog/index.c ++++ b/src/backend/catalog/index.c +@@ -39,6 +39,7 @@ + #include "catalog/pg_constraint.h" + #include "catalog/pg_operator.h" + #include "catalog/pg_opclass.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_trigger.h" + #include "catalog/pg_type.h" +@@ -90,7 +91,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, + Oid *classObjectId); + static void InitializeAttributeOids(Relation indexRelation, + int numatts, Oid indexoid); +-static void AppendAttributeTuples(Relation indexRelation, int numatts); ++static void AppendAttributeTuples(Relation indexRelation, ++ int numatts, Oid securityId); + static void UpdateIndexRelation(Oid indexoid, Oid heapoid, + IndexInfo *indexInfo, + Oid *classOids, +@@ -155,7 +157,7 @@ ConstructTupleDescriptor(Relation heapRelation, + /* + * allocate the new tuple descriptor + */ +- indexTupDesc = CreateTemplateTupleDesc(numatts, false); ++ indexTupDesc = CreateTemplateTupleDesc(numatts, false, false); + + /* + * For simple index columns, we copy the pg_attribute row from the parent +@@ -182,7 +184,8 @@ ConstructTupleDescriptor(Relation heapRelation, + * here we are indexing on a system attribute (-1...-n) + */ + from = SystemAttributeDefinition(atnum, +- heapRelation->rd_rel->relhasoids); ++ heapRelation->rd_rel->relhasoids, ++ heapRelation->rd_rel->relhassecids); + } + else + { +@@ -339,13 +342,16 @@ InitializeAttributeOids(Relation indexRelation, + * ---------------------------------------------------------------- + */ + static void +-AppendAttributeTuples(Relation indexRelation, int numatts) ++AppendAttributeTuples(Relation indexRelation, int numatts, Oid securityId) + { + Relation pg_attribute; + CatalogIndexState indstate; + TupleDesc indexTupDesc; + int i; + ++ /* copy security id */ ++ securityId = seclabelMoveSecid(AttributeRelationId, ++ RelationRelationId, securityId); + /* + * open the attribute relation and its indexes + */ +@@ -367,7 +373,8 @@ AppendAttributeTuples(Relation indexRelation, int numatts) + Assert(indexTupDesc->attrs[i]->attnum == i + 1); + Assert(indexTupDesc->attrs[i]->attcacheoff == -1); + +- InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate); ++ InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], ++ indstate, securityId); + } + + CatalogCloseIndexes(indstate); +@@ -545,6 +552,7 @@ index_create(Oid heapRelationId, + bool mapped_relation; + bool is_exclusion; + Oid namespaceId; ++ Oid securityId; + int i; + + is_exclusion = (indexInfo->ii_ExclusionOps != NULL); +@@ -682,15 +690,21 @@ index_create(Oid heapRelationId, + indexRelation->rd_rel->relam = accessMethodObjectId; + indexRelation->rd_rel->relkind = RELKIND_INDEX; + indexRelation->rd_rel->relhasoids = false; ++ indexRelation->rd_rel->relhassecids = false; + indexRelation->rd_rel->relhasexclusion = is_exclusion; + + /* ++ * Index always has same security id of the relation to be indexed on. ++ */ ++ securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(heapRelationId)); ++ ++ /* + * store index's pg_class entry + */ + InsertPgClassTuple(pg_class, indexRelation, + RelationGetRelid(indexRelation), + (Datum) 0, +- reloptions); ++ reloptions, securityId); + + /* done with pg_class */ + heap_close(pg_class, RowExclusiveLock); +@@ -706,7 +720,7 @@ index_create(Oid heapRelationId, + /* + * append ATTRIBUTE tuples for the index + */ +- AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs); ++ AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs, securityId); + + /* ---------------- + * update pg_index +diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c +index 5581346..cf1910a 100644 +--- a/src/backend/catalog/namespace.c ++++ b/src/backend/catalog/namespace.c +@@ -40,6 +40,7 @@ + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" ++#include "sepgsql/hooks.h" + #include "storage/backendid.h" + #include "storage/ipc.h" + #include "utils/acl.h" +@@ -2311,6 +2312,9 @@ LookupExplicitNamespace(const char *nspname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + nspname); + ++ /* SELinux checks */ ++ sepgsql_schema_search(namespaceId, true); ++ + return namespaceId; + } + +@@ -2903,7 +2907,8 @@ recomputeNamespacePath(void) + if (OidIsValid(namespaceId) && + !list_member_oid(oidlist, namespaceId) && + pg_namespace_aclcheck(namespaceId, roleid, +- ACL_USAGE) == ACLCHECK_OK) ++ ACL_USAGE) == ACLCHECK_OK && ++ sepgsql_schema_search(namespaceId, false)) + oidlist = lappend_oid(oidlist, namespaceId); + } + } +@@ -2930,7 +2935,8 @@ recomputeNamespacePath(void) + if (OidIsValid(namespaceId) && + !list_member_oid(oidlist, namespaceId) && + pg_namespace_aclcheck(namespaceId, roleid, +- ACL_USAGE) == ACLCHECK_OK) ++ ACL_USAGE) == ACLCHECK_OK && ++ sepgsql_schema_search(namespaceId, false)) + oidlist = lappend_oid(oidlist, namespaceId); + } + } +@@ -2996,9 +3002,12 @@ InitTempTableNamespace(void) + char namespaceName[NAMEDATALEN]; + Oid namespaceId; + Oid toastspaceId; ++ Oid secid; + + Assert(!OidIsValid(myTempNamespace)); + ++ snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); ++ + /* + * First, do permission check to see if we are authorized to make temp + * tables. We use a nonstandard error message here since "databasename: +@@ -3016,6 +3025,9 @@ InitTempTableNamespace(void) + errmsg("permission denied to create temporary tables in database \"%s\"", + get_database_name(MyDatabaseId)))); + ++ /* SELinux checks */ ++ secid = sepgsql_schema_create(namespaceName, true); ++ + /* + * Do not allow a Hot Standby slave session to make temp tables. Aside + * from problems with modifying the system catalogs, there is a naming +@@ -3031,8 +3043,6 @@ InitTempTableNamespace(void) + (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), + errmsg("cannot create temporary tables during recovery"))); + +- snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); +- + namespaceId = GetSysCacheOid1(NAMESPACENAME, + CStringGetDatum(namespaceName)); + if (!OidIsValid(namespaceId)) +@@ -3045,7 +3055,9 @@ InitTempTableNamespace(void) + * temp tables. This works because the places that access the temp + * namespace for my own backend skip permissions checks on it. + */ +- namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); ++ namespaceId = NamespaceCreate(namespaceName, ++ BOOTSTRAP_SUPERUSERID, ++ secid); + /* Advance command counter to make namespace visible */ + CommandCounterIncrement(); + } +@@ -3070,7 +3082,9 @@ InitTempTableNamespace(void) + CStringGetDatum(namespaceName)); + if (!OidIsValid(toastspaceId)) + { +- toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); ++ toastspaceId = NamespaceCreate(namespaceName, ++ BOOTSTRAP_SUPERUSERID, ++ secid); + /* Advance command counter to make namespace visible */ + CommandCounterIncrement(); + } +diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c +index 582d894..d7b6bd1 100644 +--- a/src/backend/catalog/pg_aggregate.c ++++ b/src/backend/catalog/pg_aggregate.c +@@ -27,6 +27,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_func.h" + #include "parser/parse_oper.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -67,6 +68,7 @@ AggregateCreate(const char *aggName, + Oid *fnArgs; + int nargs_transfn; + Oid procOid; ++ Oid procSecid; + TupleDesc tupDesc; + int i; + ObjectAddress myself, +@@ -161,6 +163,10 @@ AggregateCreate(const char *aggName, + } + Assert(OidIsValid(finaltype)); + ++ /* SELinux checks */ ++ procSecid = sepgsql_aggregate_create(aggName, aggNamespace, ++ transfn, finalfn); ++ + /* + * If finaltype (i.e. aggregate return type) is polymorphic, inputs must + * be polymorphic also, else parser will fail to deduce result type. +@@ -229,7 +235,8 @@ AggregateCreate(const char *aggName, + NIL, /* parameterDefaults */ + PointerGetDatum(NULL), /* proconfig */ + 1, /* procost */ +- 0); /* prorows */ ++ 0, /* prorows */ ++ procSecid); /* security-id */ + + /* + * Okay to create the pg_aggregate entry. +diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c +index 99085c9..b4c0b3a 100644 +--- a/src/backend/catalog/pg_conversion.c ++++ b/src/backend/catalog/pg_conversion.c +@@ -40,7 +40,7 @@ Oid + ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +- Oid conproc, bool def) ++ Oid conproc, bool def, Oid securityId) + { + int i; + Relation rel; +@@ -104,6 +104,8 @@ ConversionCreate(const char *conname, Oid connamespace, + + tup = heap_form_tuple(tupDesc, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + /* insert a new tuple */ + oid = simple_heap_insert(rel, tup); + Assert(OidIsValid(oid)); +diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c +index e3f18bf..572ce01 100644 +--- a/src/backend/catalog/pg_largeobject.c ++++ b/src/backend/catalog/pg_largeobject.c +@@ -21,10 +21,13 @@ + #include "catalog/dependency.h" + #include "catalog/indexing.h" + #include "catalog/pg_authid.h" ++#include "catalog/pg_description.h" + #include "catalog/pg_largeobject.h" + #include "catalog/pg_largeobject_metadata.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/toasting.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/bytea.h" + #include "utils/fmgroids.h" +@@ -40,7 +43,7 @@ + * will appear to exist with size 0. + */ + Oid +-LargeObjectCreate(Oid loid) ++LargeObjectCreate(Oid loid, Oid securityId) + { + Relation pg_lo_meta; + HeapTuple ntup; +@@ -66,6 +69,8 @@ LargeObjectCreate(Oid loid) + if (OidIsValid(loid)) + HeapTupleSetOid(ntup, loid); + ++ HeapTupleSetSecid(ntup, securityId); ++ + loid_new = simple_heap_insert(pg_lo_meta, ntup); + Assert(!OidIsValid(loid) || loid == loid_new); + +@@ -245,6 +250,64 @@ LargeObjectAlterOwner(Oid loid, Oid newOwnerId) + } + + /* ++ * LargeObjectAlterSecLabel ++ * ++ * Implementation of ALTER LARGE OBJECT xxx SECURITY LABEL ++ */ ++void ++LargeObjectAlterSecLabel(Oid loid, char *new_label) ++{ ++ Relation pg_lo_meta; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ Oid securityId; ++ ++ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, ++ RowExclusiveLock); ++ ++ ScanKeyInit(&skey, ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(loid)); ++ ++ scan = systable_beginscan(pg_lo_meta, ++ LargeObjectMetadataOidIndexId, true, ++ SnapshotNow, 1, &skey); ++ ++ oldtup = systable_getnext(scan); ++ if (!HeapTupleIsValid(oldtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("large object %u does not exist", loid))); ++ ++ /* Must be owner of the large object */ ++ if (!pg_largeobject_ownercheck(loid, GetUserId())) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("must be owner of large object %u", loid))); ++ ++ /* SELinux checks */ ++ securityId = sepgsql_largeobject_relabel(loid, new_label); ++ ++ /* update the tuple */ ++ newtup = heap_copytuple(oldtup); ++ ++ HeapTupleSetSecid(newtup, securityId); ++ ++ simple_heap_update(pg_lo_meta, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(pg_lo_meta, newtup); ++ ++ heap_freetuple(newtup); ++ ++ systable_endscan(scan); ++ ++ heap_close(pg_lo_meta, RowExclusiveLock); ++} ++ ++/* + * LargeObjectExists + * + * We don't use the system cache to for large object metadata, for fear of +diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c +index 22111a3..cba7274 100644 +--- a/src/backend/catalog/pg_namespace.c ++++ b/src/backend/catalog/pg_namespace.c +@@ -28,7 +28,7 @@ + * --------------- + */ + Oid +-NamespaceCreate(const char *nspName, Oid ownerId) ++NamespaceCreate(const char *nspName, Oid ownerId, Oid secid) + { + Relation nspdesc; + HeapTuple tup; +@@ -64,6 +64,8 @@ NamespaceCreate(const char *nspName, Oid ownerId) + tupDesc = nspdesc->rd_att; + + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + nspoid = simple_heap_insert(nspdesc, tup); + Assert(OidIsValid(nspoid)); +diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c +index 2362268..f6df55e 100644 +--- a/src/backend/catalog/pg_operator.c ++++ b/src/backend/catalog/pg_operator.c +@@ -28,6 +28,7 @@ + #include "catalog/pg_type.h" + #include "miscadmin.h" + #include "parser/parse_oper.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -204,6 +205,7 @@ OperatorShellMake(const char *operatorName, + { + Relation pg_operator_desc; + Oid operatorObjectId; ++ Oid securityId; + int i; + HeapTuple tup; + Datum values[Natts_pg_operator]; +@@ -220,6 +222,12 @@ OperatorShellMake(const char *operatorName, + errmsg("\"%s\" is not a valid operator name", + operatorName))); + ++ /* SELinux checks */ ++ securityId = sepgsql_operator_create(operatorName, InvalidOid, ++ operatorNamespace, ++ InvalidOid, InvalidOid, InvalidOid, ++ InvalidOid, InvalidOid); ++ + /* + * initialize our *nulls and *values arrays + */ +@@ -261,6 +269,8 @@ OperatorShellMake(const char *operatorName, + */ + tup = heap_form_tuple(tupDesc, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + /* + * insert our "shell" operator tuple + */ +@@ -340,6 +350,7 @@ OperatorCreate(const char *operatorName, + bool replaces[Natts_pg_operator]; + Datum values[Natts_pg_operator]; + Oid operatorObjectId; ++ Oid securityId; + bool operatorAlreadyDefined; + Oid operResultType; + Oid commutatorId, +@@ -476,6 +487,12 @@ OperatorCreate(const char *operatorName, + else + negatorId = InvalidOid; + ++ /* SELinux checks */ ++ securityId = sepgsql_operator_create(operatorName, operatorObjectId, ++ operatorNamespace, ++ procedureId, restrictionId, joinId, ++ commutatorId, negatorId); ++ + /* + * set up values in the operator tuple + */ +@@ -516,6 +533,8 @@ OperatorCreate(const char *operatorName, + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for operator %u", + operatorObjectId); ++ if (securityId != HeapTupleHasSecid(tup)) ++ elog(ERROR, "Bug? security-id was mismatched."); + + tup = heap_modify_tuple(tup, + RelationGetDescr(pg_operator_desc), +@@ -530,6 +549,8 @@ OperatorCreate(const char *operatorName, + tupDesc = pg_operator_desc->rd_att; + tup = heap_form_tuple(tupDesc, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + operatorObjectId = simple_heap_insert(pg_operator_desc, tup); + } + +diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c +index 3772c32..d99c351 100644 +--- a/src/backend/catalog/pg_proc.c ++++ b/src/backend/catalog/pg_proc.c +@@ -84,7 +84,8 @@ ProcedureCreate(const char *procedureName, + List *parameterDefaults, + Datum proconfig, + float4 procost, +- float4 prorows) ++ float4 prorows, ++ Oid prosecid) + { + Oid retval; + int parameterCount; +@@ -364,6 +365,8 @@ ProcedureCreate(const char *procedureName, + if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), proowner)) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + procedureName); ++ if (prosecid != HeapTupleGetSecid(oldtup)) ++ elog(ERROR, "Bug? security-id was tried to be changed."); + + /* + * Not okay to change the return type of the existing proc, since +@@ -548,6 +551,9 @@ ProcedureCreate(const char *procedureName, + nulls[Anum_pg_proc_proacl - 1] = true; + + tup = heap_form_tuple(tupDesc, values, nulls); ++ ++ HeapTupleSetSecid(tup, prosecid); ++ + simple_heap_insert(rel, tup); + is_update = false; + } +diff --git a/src/backend/catalog/pg_seclabel.c b/src/backend/catalog/pg_seclabel.c +new file mode 100644 +index 0000000..4816635 +--- /dev/null ++++ b/src/backend/catalog/pg_seclabel.c +@@ -0,0 +1,646 @@ ++/* ++ * pg_seclabel.c ++ * ++ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++ ++#include "access/genam.h" ++#include "access/heapam.h" ++#include "access/sysattr.h" ++#include "access/tupdesc.h" ++#include "catalog/catalog.h" ++#include "catalog/indexing.h" ++#include "catalog/pg_aggregate.h" ++#include "catalog/pg_amop.h" ++#include "catalog/pg_amproc.h" ++#include "catalog/pg_attrdef.h" ++#include "catalog/pg_auth_members.h" ++#include "catalog/pg_constraint.h" ++#include "catalog/pg_db_role_setting.h" ++#include "catalog/pg_depend.h" ++#include "catalog/pg_description.h" ++#include "catalog/pg_enum.h" ++#include "catalog/pg_index.h" ++#include "catalog/pg_inherits.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_rewrite.h" ++#include "catalog/pg_seclabel.h" ++#include "catalog/pg_shdepend.h" ++#include "catalog/pg_shdescription.h" ++#include "catalog/pg_trigger.h" ++#include "catalog/pg_ts_config_map.h" ++#include "executor/spi.h" ++#include "miscadmin.h" ++#include "sepgsql/sepgsql.h" ++#include "sepgsql/hooks.h" ++#include "utils/builtins.h" ++#include "utils/fmgroids.h" ++#include "utils/lsyscache.h" ++#include "utils/rel.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++ ++bool ignore_security_label_input; ++ ++void ++seclabelOnCreateDatabase(Oid src_datid, Oid dst_datid) ++{ ++ Relation rel; ++ ScanKeyData keys[1]; ++ SysScanDesc scan; ++ HeapTuple oldtup, newtup; ++ Datum values[Natts_pg_seclabel]; ++ bool nulls[Natts_pg_seclabel]; ++ bool replaces[Natts_pg_seclabel]; ++ ++ /* Scan all entries with pg_seclabel.datid = src_datid */ ++ ScanKeyInit(&keys[0], ++ Anum_pg_seclabel_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(src_datid)); ++ ++ rel = heap_open(SecLabelRelationId, RowExclusiveLock); ++ ++ scan = systable_beginscan(rel, SecLabelSecidIndexId, true, ++ SnapshotNow, 1, keys); ++ ++ /* corresponding entries will be inserted with new datid */ ++ memset(values, 0, sizeof(values)); ++ memset(nulls, false, sizeof(nulls)); ++ memset(replaces, false, sizeof(replaces)); ++ ++ values[Anum_pg_seclabel_datid - 1] = ObjectIdGetDatum(dst_datid); ++ replaces[Anum_pg_seclabel_datid - 1] = true; ++ while (HeapTupleIsValid(oldtup = systable_getnext(scan))) ++ { ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ values, nulls, replaces); ++ simple_heap_insert(rel, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ } ++ systable_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++void ++seclabelOnDropDatabase(Oid datid) ++{ ++ Relation rel; ++ ScanKeyData keys[1]; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ ++ /* Scan all entries with pg_seclabel.datid = datid */ ++ ScanKeyInit(&keys[0], ++ Anum_pg_seclabel_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(datid)); ++ ++ rel = heap_open(SecLabelRelationId, RowExclusiveLock); ++ ++ scan = systable_beginscan(rel, SecLabelSecidIndexId, true, ++ SnapshotNow, 1, keys); ++ ++ while (HeapTupleIsValid(tuple = systable_getnext(scan))) ++ { ++ simple_heap_delete(rel, &tuple->t_self); ++ } ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++void ++seclabelOnDropTable(Oid relid) ++{ ++ Relation rel; ++ SysScanDesc scan; ++ ScanKeyData key[2]; ++ HeapTuple tuple; ++ Oid database_oid; ++ ++ database_oid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); ++ ScanKeyInit(&key[0], ++ Anum_pg_seclabel_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(database_oid)); ++ ScanKeyInit(&key[1], ++ Anum_pg_seclabel_relid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relid)); ++ ++ rel = heap_open(SecLabelRelationId, RowExclusiveLock); ++ scan = systable_beginscan(rel, SecLabelLabelIndexId, true, ++ SnapshotNow, 2, key); ++ ++ while (HeapTupleIsValid(tuple = systable_getnext(scan))) ++ simple_heap_delete(rel, &tuple->t_self); ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++/* ++ * XXX - it should be replaced by BKI scripts ++ */ ++bool ++seclabelCatalogHasSysAttr(Oid relOid) ++{ ++ switch (relOid) ++ { ++ case AggregateRelationId: ++ /* pg_aggregate is property of pg_proc */ ++ case AccessMethodOperatorRelationId: ++ /* pg_amop is property of pg_opfamily */ ++ case AccessMethodProcedureRelationId: ++ /* pg_amproc is property of pg_opfamily */ ++ case AttrDefaultRelationId: ++ /* pg_attrdef is property of pg_attribute */ ++ case AuthMemRelationId: ++ /* pg_auth_members is property of pg_auth */ ++ case ConstraintRelationId: ++ /* ++ * CHECK constraint is property of pg_class ++ * DOMAIN constraint is property of pg_type ++ * Global assertion is property of pg_database ++ */ ++ case DbRoleSettingRelationId: ++ /* pg_db_role_setting is property of pg_auth or pg_database */ ++ case DependRelationId: ++ /* property of the depending object */ ++ case DescriptionRelationId: ++ /* property of the object commented on */ ++ case EnumRelationId: ++ /* pg_enum is property of pg_type */ ++ case IndexRelationId: ++ /* pg_index is property of pg_class with RELKIND_INDEX */ ++ case InheritsRelationId: ++ /* pg_inherits is property of the child relation */ ++ case LargeObjectRelationId: ++ /* pg_largeobject is data chunk of pg_largeobject_metadata */ ++ case RewriteRelationId: ++ /* pg_rewrite is property of pg_class */ ++ case SecLabelRelationId: ++ /* No security attribute has no security label */ ++ case SharedDependRelationId: ++ /* property of the depending shared object */ ++ case SharedDescriptionRelationId: ++ /* property of the shared object commented on */ ++ case TriggerRelationId: ++ /* pg_trigger is property of pg_class */ ++ case TSConfigMapRelationId: ++ /* pg_ts_config_map is property of pg_ts_config */ ++ return false; ++ ++ default: ++ return true; ++ } ++} ++ ++Oid * ++seclabelMakeRelationDefaults(TupleDesc tupdesc, List *supOids) ++{ ++ ListCell *l; ++ Oid *secLabels; ++ Oid securityId; ++ int index, attno, nitems; ++ ++ nitems = tupdesc->natts - FirstLowInvalidHeapAttributeNumber; ++ secLabels = palloc0(sizeof(Oid) * nitems); ++ ++ foreach (l, supOids) ++ { ++ Oid relOid = lfirst_oid(l); ++ ++ securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); ++ ++ if (!OidIsValid(secLabels[0])) ++ secLabels[0] = securityId; ++ else if (!seclabelCompareSecid(RelationRelationId, secLabels[0], ++ RelationRelationId, securityId)) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("cannot inherit relations with different label"))); ++ ++ for (index = 1; index < nitems; index++) ++ { ++ attno = index + FirstLowInvalidHeapAttributeNumber; ++ ++ if (attno < 0) ++ securityId = GetSysCacheSecid2(ATTNUM, ++ ObjectIdGetDatum(relOid), ++ Int16GetDatum(attno)); ++ else ++ { ++ const char *attname = NameStr(tupdesc->attrs[attno]->attname); ++ securityId = GetSysCacheSecid2(ATTNAME, ++ ObjectIdGetDatum(relOid), ++ PointerGetDatum(attname)); ++ } ++ ++ if (!OidIsValid(securityId)) ++ continue; ++ ++ if (!OidIsValid(secLabels[index])) ++ secLabels[index] = securityId; ++ else if (!seclabelCompareSecid(AttributeRelationId, secLabels[index], ++ AttributeRelationId, securityId)) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("cannot inherit attribute with different label"))); ++ } ++ } ++ return secLabels; ++} ++ ++Oid * ++seclabelMakeToastDefaults(TupleDesc tupdesc, Oid relOid) ++{ ++ Oid *secLabels; ++ Oid securityId; ++ int index, nitems; ++ ++ nitems = tupdesc->natts + 1 - FirstLowInvalidHeapAttributeNumber; ++ secLabels = palloc0(sizeof(Oid) * nitems); ++ ++ securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); ++ secLabels[0] = securityId; ++ ++ securityId = seclabelMoveSecid(AttributeRelationId, ++ RelationRelationId, ++ securityId); ++ for (index = 1; index < nitems; index++) ++ secLabels[index] = securityId; ++ ++ return secLabels; ++} ++ ++Oid ++seclabelGetNewSecid(Relation rel, HeapTuple tuple) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled() && ++ !IsBootstrapProcessingMode()) ++ { ++ return sepgsql_get_default_secid(rel, tuple); ++ } ++#endif ++ return InvalidOid; ++} ++ ++static Oid ++inputSecurityLabel(Oid relid, const char *seclabel) ++{ ++ LOCKMODE lockmode = AccessShareLock; ++ Relation rel; ++ ScanKeyData skey[3]; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ Oid datid; ++ Oid secid; ++ Datum values[Natts_pg_seclabel]; ++ bool nulls[Natts_pg_seclabel]; ++ ++ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); ++ ++retry: ++ /* ++ * Lookup pg_seclabel first, then insert a new entry if not found. ++ * An exclusive lock is not necessary for the first read-only path, ++ * and we assume most of trials are read-only. ++ */ ++ rel = heap_open(SecLabelRelationId, lockmode); ++ ++ ScanKeyInit(&skey[0], ++ Anum_pg_seclabel_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(datid)); ++ ScanKeyInit(&skey[1], ++ Anum_pg_seclabel_relid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relid)); ++ ScanKeyInit(&skey[2], ++ Anum_pg_seclabel_label, ++ BTEqualStrategyNumber, F_TEXTEQ, ++ CStringGetTextDatum(seclabel)); ++ ++ scan = systable_beginscan(rel, SecLabelLabelIndexId, true, ++ SnapshotToast, 3, skey); ++ ++ tuple = systable_getnext(scan); ++ if (HeapTupleIsValid(tuple)) ++ { ++ secid = ((Form_pg_seclabel) GETSTRUCT(tuple))->secid; ++ systable_endscan(scan); ++ heap_close(rel, lockmode); ++ return secid; ++ } ++ ++ /* ++ * If not exist, try to insert a new entry. ++ */ ++ if (lockmode == AccessShareLock) ++ { ++ systable_endscan(scan); ++ heap_close(rel, lockmode); ++ lockmode = RowExclusiveLock; ++ goto retry; ++ } ++ ++ memset(nulls, false, sizeof(nulls)); ++ secid = GetNewOidWithIndex(rel, SecLabelSecidIndexId, ++ Anum_pg_seclabel_secid); ++ values[Anum_pg_seclabel_secid - 1] = ObjectIdGetDatum(secid); ++ values[Anum_pg_seclabel_datid - 1] = ObjectIdGetDatum(datid); ++ values[Anum_pg_seclabel_relid - 1] = ObjectIdGetDatum(relid); ++ values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(seclabel); ++ ++ tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); ++ ++ simple_heap_insert(rel, tuple); ++ ++ CatalogUpdateIndexes(rel, tuple); ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, lockmode); ++ ++ return secid; ++} ++ ++static char * ++outputSecurityLabel(Oid relid, Oid secid) ++{ ++ Relation rel; ++ ScanKeyData skey[3]; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ Oid datid; ++ char *result = NULL; ++ ++ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); ++ ++ /* ++ * Lookup pg_seclabel for the given datid/relid/secid ++ */ ++ rel = heap_open(SecLabelRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey[0], ++ Anum_pg_seclabel_secid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(secid)); ++ ScanKeyInit(&skey[1], ++ Anum_pg_seclabel_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(datid)); ++ ScanKeyInit(&skey[2], ++ Anum_pg_seclabel_relid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relid)); ++ ++ scan = systable_beginscan(rel, SecLabelSecidIndexId, true, ++ SnapshotToast, 3, skey); ++ tuple = systable_getnext(scan); ++ if (HeapTupleIsValid(tuple)) ++ { ++ Datum datum; ++ bool isnull; ++ ++ datum = heap_getattr(tuple, ++ Anum_pg_seclabel_label, ++ RelationGetDescr(rel), &isnull); ++ if (!isnull) ++ result = TextDatumGetCString(datum); ++ } ++ systable_endscan(scan); ++ ++ heap_close(rel, AccessShareLock); ++ ++ return result; ++} ++ ++Oid ++seclabelRawInput(Oid relid, char *seclabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ seclabel = sepgsql_rawlabel_in(seclabel); ++#endif ++ return inputSecurityLabel(relid, seclabel); ++} ++ ++char * ++seclabelRawOutput(Oid relid, Oid secid) ++{ ++ char *seclabel = outputSecurityLabel(relid, secid); ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ seclabel = sepgsql_rawlabel_out(seclabel); ++#endif ++ return seclabel; ++} ++ ++Oid ++seclabelTransInput(Oid relid, char *seclabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ seclabel = sepgsql_mcstrans_in(seclabel); ++#endif ++ return seclabelRawInput(relid, seclabel); ++} ++ ++char * ++seclabelTransOutput(Oid relid, Oid secid) ++{ ++ char *seclabel = seclabelRawOutput(relid, secid); ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ seclabel = sepgsql_mcstrans_out(seclabel); ++#endif ++ return seclabel; ++} ++ ++Oid ++seclabelMoveSecid(Oid dst_relid, Oid src_relid, Oid secid) ++{ ++ char *label = seclabelRawOutput(src_relid, secid); ++ ++ if (!label) ++ return InvalidOid; ++ ++ secid = seclabelRawInput(dst_relid, label); ++ ++ pfree(label); ++ ++ return secid; ++} ++ ++bool ++seclabelCompareSecid(Oid relid1, Oid secid1, Oid relid2, Oid secid2) ++{ ++ char *label1 = seclabelRawOutput(relid1, secid1); ++ char *label2 = seclabelRawOutput(relid2, secid2); ++ bool retval = false; ++ ++ if (label1 && label2 && strcmp(label1, label2) == 0) ++ retval = true; ++ else if (!label1 && !label2) ++ retval = false; ++ ++ if (label1) ++ pfree(label1); ++ if (label2) ++ pfree(label2); ++ ++ return retval; ++} ++ ++Datum ++seclabelSysattOutput(Oid relid, HeapTuple tuple) ++{ ++ Oid secid; ++ char *seclabel; ++ ++ secid = HeapTupleGetSecid(tuple); ++ ++ seclabel = seclabelTransOutput(relid, secid); ++ if (!seclabel) ++ seclabel = "unlabeled"; ++ ++ return CStringGetTextDatum(seclabel); ++} ++ ++/* ++ * seclabelRelationReclaim ++ * ++ * It reclaims security labels already referenced to. ++ * It has to be called under the VACUUM FULL context that means the relation ++ * to be reclaimed is already locked exclusively. ++ */ ++void ++seclabelRelationReclaim(Oid relOid) ++{ ++ StringInfoData query; ++ const char *nspname_reclaimed; ++ const char *relname_reclaimed; ++ const char *nspname_pg_seclabel; ++ const char *relname_pg_seclabel; ++ const char *attname_secid; ++ const char *attname_datid; ++ const char *attname_relid; ++ const char *attname_label; ++ const char *nspname_to_secid; ++ const char *proname_to_secid; ++ Oid databaseId; ++ Oid namespaceId; ++ int index; ++ int save_sepgsql_mode; ++ ++ if (SPI_connect() != SPI_OK_CONNECT) ++ elog(ERROR, "SPI_connect() failed"); ++ ++ /* ++ * DELETE orphan entries ++ */ ++ databaseId = (IsSharedRelation(relOid) ? InvalidOid : MyDatabaseId); ++ ++ namespaceId = get_rel_namespace(relOid); ++ nspname_reclaimed = get_namespace_name(namespaceId); ++ relname_reclaimed = get_rel_name(relOid); ++ ++ namespaceId = get_rel_namespace(SecLabelRelationId); ++ nspname_pg_seclabel = get_namespace_name(namespaceId); ++ relname_pg_seclabel = get_rel_name(SecLabelRelationId); ++ ++ attname_secid = get_attname(SecLabelRelationId, Anum_pg_seclabel_secid); ++ attname_datid = get_attname(SecLabelRelationId, Anum_pg_seclabel_datid); ++ attname_relid = get_attname(SecLabelRelationId, Anum_pg_seclabel_relid); ++ attname_label = get_attname(SecLabelRelationId, Anum_pg_seclabel_label); ++ ++ namespaceId = get_func_namespace(F_SECLABEL_TO_SECID); ++ nspname_to_secid = get_namespace_name(namespaceId); ++ proname_to_secid = get_func_name(F_SECLABEL_TO_SECID); ++ ++ initStringInfo(&query); ++ appendStringInfo(&query, ++ "DELETE FROM %s.%s " ++ "WHERE %s = %u AND %s = %u AND %s NOT IN " ++ "(SELECT %s.%s(%s) FROM ONLY %s.%s) " ++ "RETURNING %s,%s", ++ quote_identifier(nspname_pg_seclabel), ++ quote_identifier(relname_pg_seclabel), ++ quote_identifier(attname_datid), ++ databaseId, ++ quote_identifier(attname_relid), ++ relOid, ++ quote_identifier(attname_secid), ++ quote_identifier(nspname_to_secid), ++ quote_identifier(proname_to_secid), ++ quote_identifier(relname_reclaimed), ++ quote_identifier(nspname_reclaimed), ++ quote_identifier(relname_reclaimed), ++ quote_identifier(attname_secid), ++ quote_identifier(attname_label)); ++ /* ++ * Run the query ++ */ ++ elog(DEBUG1, "query: %s", query.data); ++ ++ save_sepgsql_mode = sepostgresql_mode; ++ ++ PG_TRY(); ++ { ++ sepostgresql_mode = SEPGSQL_MODE_INTERNAL; ++ ++ if (SPI_execute(query.data, false, 0) != SPI_OK_DELETE_RETURNING) ++ elog(ERROR, "Failed to run: %s", query.data); ++ } ++ PG_CATCH(); ++ { ++ sepostgresql_mode = save_sepgsql_mode; ++ ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ ++ sepostgresql_mode = save_sepgsql_mode; ++ ++ for (index = 0; index < SPI_processed; index++) ++ { ++ HeapTuple tuple = SPI_tuptable->vals[index]; ++ char *label; ++ Oid secid; ++ Datum datum; ++ bool isnull; ++ ++ datum = heap_getattr(tuple, 1, SPI_tuptable->tupdesc, &isnull); ++ secid = (isnull ? InvalidOid : DatumGetObjectId(datum)); ++ ++ datum = heap_getattr(tuple, 2, SPI_tuptable->tupdesc, &isnull); ++ label = (isnull ? NULL : TextDatumGetCString(datum)); ++ ++ elog(DEBUG1, "seclabel: \"%s\"was reclaimed (secid=%u)", ++ label, secid); ++ } ++ ++ if (SPI_finish() != SPI_OK_FINISH) ++ elog(ERROR, "SPI_finish() failed"); ++} ++ ++Datum ++seclabel_to_secid(PG_FUNCTION_ARGS) ++{ ++ HeapTupleHeader htup = PG_GETARG_HEAPTUPLEHEADER(0); ++ ++ PG_RETURN_OID(HeapTupleHeaderGetSecid(htup)); ++} +diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c +index 76f9e06..0b4dcc3 100644 +--- a/src/backend/catalog/pg_type.c ++++ b/src/backend/catalog/pg_type.c +@@ -25,6 +25,7 @@ + #include "commands/typecmds.h" + #include "miscadmin.h" + #include "parser/scansup.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -57,10 +58,17 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) + Datum values[Natts_pg_type]; + bool nulls[Natts_pg_type]; + Oid typoid; ++ Oid secid; + NameData name; + + Assert(PointerIsValid(typeName)); + ++ /* SELinux checks */ ++ secid = sepgsql_type_create(typeName, InvalidOid, ++ typeNamespace, TYPTYPE_PSEUDO, ++ F_SHELL_IN, F_SHELL_OUT, ++ InvalidOid, InvalidOid, ++ InvalidOid, InvalidOid, InvalidOid); + /* + * open pg_type + */ +@@ -126,6 +134,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) + binary_upgrade_next_pg_type_oid = InvalidOid; + } + ++ HeapTupleSetSecid(tup, secid); ++ + /* + * insert the tuple in the relation and get the tuple's oid. + */ +@@ -204,7 +214,8 @@ TypeCreate(Oid newTypeOid, + char storage, + int32 typeMod, + int32 typNDims, /* Array dimensions for baseType */ +- bool typeNotNull) ++ bool typeNotNull, ++ Oid securityId) + { + Relation pg_type_desc; + Oid typeObjectId; +@@ -389,6 +400,8 @@ TypeCreate(Oid newTypeOid, + */ + if (((Form_pg_type) GETSTRUCT(tup))->typowner != ownerId) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, typeName); ++ if (HeapTupleGetSecid(tup) != securityId) ++ elog(ERROR, "Bug? security-id was mismatched"); + + /* trouble if caller wanted to force the OID */ + if (OidIsValid(newTypeOid)) +@@ -425,6 +438,8 @@ TypeCreate(Oid newTypeOid, + } + /* else allow system to assign oid */ + ++ HeapTupleSetSecid(tup, securityId); ++ + typeObjectId = simple_heap_insert(pg_type_desc, tup); + } + +diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c +index 435dfdd..5d86354 100644 +--- a/src/backend/catalog/toasting.c ++++ b/src/backend/catalog/toasting.c +@@ -24,6 +24,7 @@ + #include "catalog/namespace.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_opclass.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_type.h" + #include "catalog/toasting.h" + #include "miscadmin.h" +@@ -127,6 +128,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio + int16 coloptions[2]; + ObjectAddress baseobject, + toastobject; ++ Oid *secLabels; + + /* + * Toast table is shared if and only if its parent is. +@@ -168,7 +170,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio + "pg_toast_%u_index", relOid); + + /* this is pretty painful... need a tuple descriptor */ +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, + "chunk_id", + OIDOID, +@@ -206,6 +208,11 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio + binary_upgrade_next_pg_type_toast_oid = InvalidOid; + } + ++ /* ++ * Toast inherits security-id from the heap relation ++ */ ++ secLabels = seclabelMakeToastDefaults(tupdesc, relOid); ++ + toast_relid = heap_create_with_catalog(toast_relname, + namespaceid, + rel->rd_rel->reltablespace, +@@ -223,7 +230,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio + ONCOMMIT_NOOP, + reloptions, + false, +- true); ++ true, ++ secLabels); + + /* make the toast relation visible, else index creation will fail */ + CommandCounterIncrement(); +diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c +index 4964fb3..390a1c1 100644 +--- a/src/backend/commands/aggregatecmds.c ++++ b/src/backend/commands/aggregatecmds.c +@@ -32,6 +32,7 @@ + #include "miscadmin.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -307,6 +308,9 @@ RenameAggregate(List *name, List *args, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_proc_alter_rename(procOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname); + simple_heap_update(rel, &tup->t_self, tup); +diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c +index 17e1e77..c1c8bfd 100644 +--- a/src/backend/commands/alter.c ++++ b/src/backend/commands/alter.c +@@ -289,3 +289,64 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) + (int) stmt->objectType); + } + } ++ ++/* ++ * ExecAlterSecLabelStmt ++ * ++ * Execute ALTER xxx SECURITY LABEL TO statement ++ */ ++void ++ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt) ++{ ++ const char *name; ++ char *seclabel; ++ ++ Assert(IsA(stmt->secLabel, String)); ++ seclabel = strVal(stmt->secLabel); ++ ++ switch (stmt->objectType) ++ { ++ case OBJECT_DATABASE: ++ name = strVal(linitial(stmt->object)); ++ AlterDatabaseSecLabel(name, seclabel); ++ break; ++ ++ case OBJECT_SCHEMA: ++ name = strVal(linitial(stmt->object)); ++ AlterSchemaSecLabel(name, seclabel); ++ break; ++ ++ case OBJECT_TABLE: ++ case OBJECT_SEQUENCE: ++ case OBJECT_VIEW: ++ case OBJECT_COLUMN: ++ AlterRelationSecLabel(stmt->relation, stmt->addname, ++ stmt->objectType, seclabel); ++ break; ++ ++ case OBJECT_AGGREGATE: ++ AlterFunctionSecLabel(stmt->object, stmt->objarg, true, seclabel); ++ break; ++ ++ case OBJECT_FUNCTION: ++ AlterFunctionSecLabel(stmt->object, stmt->objarg, false, seclabel); ++ break; ++ ++ case OBJECT_LARGEOBJECT: ++ LargeObjectAlterSecLabel(intVal(linitial(stmt->object)), seclabel); ++ break; ++ ++ case OBJECT_TYPE: ++ case OBJECT_DOMAIN: ++ AlterTypeSecLabel(stmt->object, seclabel); ++ break; ++ ++ case OBJECT_TABLESPACE: ++ AlterTableSpaceSecLabel(strVal(linitial(stmt->object)), seclabel); ++ break; ++ ++ default: ++ elog(ERROR, "unrecognized AlterSecLabelStmt type: %d", ++ (int) stmt->objectType); ++ } ++} +diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c +index 30a00ab..da39665 100644 +--- a/src/backend/commands/cluster.c ++++ b/src/backend/commands/cluster.c +@@ -30,12 +30,14 @@ + #include "catalog/indexing.h" + #include "catalog/namespace.h" + #include "catalog/pg_namespace.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/toasting.h" + #include "commands/cluster.h" + #include "commands/tablecmds.h" + #include "commands/trigger.h" + #include "commands/vacuum.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/procarray.h" + #include "storage/smgr.h" +@@ -116,6 +118,9 @@ cluster(ClusterStmt *stmt, bool isTopLevel) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + ++ /* SELinux checks */ ++ sepgsql_relation_cluster(tableOid, true); ++ + /* + * Reject clustering a remote temp table ... their local buffer + * manager is not going to cope. +@@ -290,7 +295,8 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose, + Form_pg_index indexForm; + + /* Check that the user still owns the relation */ +- if (!pg_class_ownercheck(tableOid, GetUserId())) ++ if (!pg_class_ownercheck(tableOid, GetUserId()) || ++ !sepgsql_relation_cluster(tableOid, false)) + { + relation_close(OldHeap, AccessExclusiveLock); + return; +@@ -632,6 +638,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) + HeapTuple tuple; + Datum reloptions; + bool isNull; ++ Oid *secLabels; + + OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock); + OldHeapDesc = RelationGetDescr(OldHeap); +@@ -657,6 +664,11 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) + reloptions = (Datum) 0; + + /* ++ * The new heap copies all the security-id from the original ++ */ ++ secLabels = seclabelMakeRelationDefaults(tupdesc, ++ list_make1_oid(OIDOldHeap)); ++ /* + * Create the new heap, using a temporary name in the same namespace as + * the existing table. NOTE: there is some risk of collision with user + * relnames. Working around this seems more trouble than it's worth; in +@@ -687,7 +699,8 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) + ONCOMMIT_NOOP, + reloptions, + false, +- true); ++ true, ++ secLabels); + + ReleaseSysCache(tuple); + +@@ -994,6 +1007,9 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, + /* Preserve OID, if any */ + if (NewHeap->rd_rel->relhasoids) + HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); ++ /* Preserve security-id, if any */ ++ if (NewHeap->rd_rel->relhassecids) ++ HeapTupleSetSecid(copiedTuple, HeapTupleGetSecid(tuple)); + + /* The heap rewrite module does the rest */ + rewrite_heap_tuple(rwstate, tuple, copiedTuple); +@@ -1481,7 +1497,8 @@ get_tables_to_cluster(MemoryContext cluster_context) + { + index = (Form_pg_index) GETSTRUCT(indexTuple); + +- if (!pg_class_ownercheck(index->indrelid, GetUserId())) ++ if (!pg_class_ownercheck(index->indrelid, GetUserId()) || ++ !sepgsql_relation_cluster(index->indrelid, false)) + continue; + + /* +diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c +index 64792f2..6d71642 100644 +--- a/src/backend/commands/comment.c ++++ b/src/backend/commands/comment.c +@@ -49,6 +49,7 @@ + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -548,6 +549,9 @@ CommentRelation(int objtype, List *relname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(relation)); + ++ /* SELinux checks */ ++ sepgsql_relation_comment(RelationGetRelid(relation)); ++ + /* Next, verify that the relation type matches the intent */ + + switch (objtype) +@@ -651,6 +655,9 @@ CommentAttribute(List *qualname, char *comment) + errmsg("column \"%s\" of relation \"%s\" does not exist", + attrname, RelationGetRelationName(relation)))); + ++ /* SELinux checks */ ++ sepgsql_attribute_comment(RelationGetRelid(relation), attnum); ++ + /* Create the comment using the relation's oid */ + CreateComments(RelationGetRelid(relation), RelationRelationId, + (int32) attnum, comment); +@@ -704,6 +711,9 @@ CommentDatabase(List *qualname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + database); + ++ /* SELinux checks */ ++ sepgsql_database_comment(oid); ++ + /* Call CreateSharedComments() to create/drop the comments */ + CreateSharedComments(oid, DatabaseRelationId, comment); + } +@@ -742,6 +752,9 @@ CommentTablespace(List *qualname, char *comment) + if (!pg_tablespace_ownercheck(oid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, tablespace); + ++ /* SELinux checks */ ++ sepgsql_tablespace_comment(oid); ++ + /* Call CreateSharedComments() to create/drop the comments */ + CreateSharedComments(oid, TableSpaceRelationId, comment); + } +@@ -774,6 +787,9 @@ CommentRole(List *qualname, char *comment) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be member of role \"%s\" to comment upon it", role))); + ++ /* SELinux checks */ ++ sepgsql_role_comment(oid); ++ + /* Call CreateSharedComments() to create/drop the comments */ + CreateSharedComments(oid, AuthIdRelationId, comment); + } +@@ -810,6 +826,9 @@ CommentNamespace(List *qualname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, + namespace); + ++ /* SELinux checks */ ++ sepgsql_schema_comment(oid); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(oid, NamespaceRelationId, 0, comment); + } +@@ -919,6 +938,9 @@ CommentRule(List *qualname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(reloid)); + ++ /* SELinux checks */ ++ sepgsql_rule_comment(reloid, rulename); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(ruleoid, RewriteRelationId, 0, comment); + +@@ -953,6 +975,9 @@ CommentType(List *typename, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, + format_type_be(oid)); + ++ /* SELinux checks */ ++ sepgsql_type_comment(oid); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(oid, TypeRelationId, 0, comment); + } +@@ -977,6 +1002,9 @@ CommentAggregate(List *aggregate, List *arguments, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(aggregate)); + ++ /* SELinux checks */ ++ sepgsql_proc_comment(oid); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(oid, ProcedureRelationId, 0, comment); + } +@@ -1005,6 +1033,9 @@ CommentProc(List *function, List *arguments, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(function)); + ++ /* SELinux checks */ ++ sepgsql_proc_comment(oid); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(oid, ProcedureRelationId, 0, comment); + } +@@ -1036,6 +1067,9 @@ CommentOperator(List *opername, List *arguments, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, + NameListToString(opername)); + ++ /* SELinux checks */ ++ sepgsql_operator_comment(oid); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(oid, OperatorRelationId, 0, comment); + } +@@ -1080,6 +1114,9 @@ CommentTrigger(List *qualname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(relation)); + ++ /* SELinux checks */ ++ sepgsql_trigger_comment(RelationGetRelid(relation), trigname); ++ + /* + * Fetch the trigger tuple from pg_trigger. There can be only one because + * of the unique index. +@@ -1153,6 +1190,9 @@ CommentConstraint(List *qualname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(relation)); + ++ /* SELinux checks */ ++ sepgsql_constraint_comment(RelationGetRelid(relation), conName); ++ + conOid = GetConstraintByName(RelationGetRelid(relation), conName); + + /* Call CreateComments() to create/drop the comments */ +@@ -1188,6 +1228,9 @@ CommentConversion(List *qualname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, + NameListToString(qualname)); + ++ /* SELinux checks */ ++ sepgsql_conversion_comment(conversionOid); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(conversionOid, ConversionRelationId, 0, comment); + } +@@ -1304,6 +1347,9 @@ CommentOpClass(List *qualname, List *arguments, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, + NameListToString(qualname)); + ++ /* SELinux checks */ ++ sepgsql_opclass_comment(opcID); ++ + ReleaseSysCache(tuple); + + /* Call CreateComments() to create/drop the comments */ +@@ -1385,6 +1431,9 @@ CommentOpFamily(List *qualname, List *arguments, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, + NameListToString(qualname)); + ++ /* SELinux checks */ ++ sepgsql_opfamily_comment(opfID); ++ + ReleaseSysCache(tuple); + + /* Call CreateComments() to create/drop the comments */ +@@ -1505,6 +1554,9 @@ CommentCast(List *qualname, List *arguments, char *comment) + format_type_be(sourcetypeid), + format_type_be(targettypeid)))); + ++ /* SELinux checks */ ++ sepgsql_cast_comment(sourcetypeid, targettypeid); ++ + ReleaseSysCache(tuple); + + /* Call CreateComments() to create/drop the comments */ +@@ -1522,6 +1574,8 @@ CommentTSParser(List *qualname, char *comment) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to comment on text search parser"))); ++ /* SELinux checks */ ++ sepgsql_ts_parser_comment(prsId); + + CreateComments(prsId, TSParserRelationId, 0, comment); + } +@@ -1536,6 +1590,8 @@ CommentTSDictionary(List *qualname, char *comment) + if (!pg_ts_dict_ownercheck(dictId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, + NameListToString(qualname)); ++ /* SELinux checks */ ++ sepgsql_ts_dict_comment(dictId); + + CreateComments(dictId, TSDictionaryRelationId, 0, comment); + } +@@ -1551,6 +1607,8 @@ CommentTSTemplate(List *qualname, char *comment) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to comment on text search template"))); ++ /* SELinux checks */ ++ sepgsql_ts_template_comment(tmplId); + + CreateComments(tmplId, TSTemplateRelationId, 0, comment); + } +@@ -1565,6 +1623,8 @@ CommentTSConfiguration(List *qualname, char *comment) + if (!pg_ts_config_ownercheck(cfgId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, + NameListToString(qualname)); ++ /* SELinux checks */ ++ sepgsql_ts_config_comment(cfgId); + + CreateComments(cfgId, TSConfigRelationId, 0, comment); + } +diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c +index 57ddab0..0c10a64 100644 +--- a/src/backend/commands/conversioncmds.c ++++ b/src/backend/commands/conversioncmds.c +@@ -24,6 +24,7 @@ + #include "mb/pg_wchar.h" + #include "miscadmin.h" + #include "parser/parse_func.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -45,6 +46,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) + int from_encoding; + int to_encoding; + Oid funcoid; ++ Oid secid; + const char *from_encoding_name = stmt->for_encoding_name; + const char *to_encoding_name = stmt->to_encoding_name; + List *func_name = stmt->func_name; +@@ -96,6 +98,10 @@ CreateConversionCommand(CreateConversionStmt *stmt) + aclcheck_error(aclresult, ACL_KIND_PROC, + NameListToString(func_name)); + ++ /* SELinux checks */ ++ secid = sepgsql_conversion_create(conversion_name, ++ namespaceId, funcoid); ++ + /* + * Check that the conversion function is suitable for the requested source + * and target encodings. We do that by calling the function with an empty +@@ -114,7 +120,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) + * name) + */ + ConversionCreate(conversion_name, namespaceId, GetUserId(), +- from_encoding, to_encoding, funcoid, stmt->def); ++ from_encoding, to_encoding, funcoid, stmt->def, secid); + } + + /* +@@ -174,6 +180,9 @@ DropConversionsCommand(DropStmt *drop) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, + NameStr(con->conname)); + ++ /* SELinux checks */ ++ sepgsql_conversion_drop(conversionOid, false); ++ + object.classId = ConversionRelationId; + object.objectId = conversionOid; + object.objectSubId = 0; +@@ -235,6 +244,9 @@ RenameConversion(List *name, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_conversion_alter_rename(conversionOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -329,6 +341,8 @@ AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(convForm->connamespace)); + } ++ /* SELinux checks */ ++ sepgsql_conversion_alter(HeapTupleGetOid(tup)); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c +index 84a83f1..7cf7a89 100644 +--- a/src/backend/commands/copy.c ++++ b/src/backend/commands/copy.c +@@ -22,7 +22,10 @@ + + #include "access/heapam.h" + #include "access/xact.h" ++#include "access/sysattr.h" ++#include "catalog/heap.h" + #include "catalog/namespace.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_type.h" + #include "commands/copy.h" + #include "commands/defrem.h" +@@ -35,6 +38,7 @@ + #include "optimizer/planner.h" + #include "parser/parse_relation.h" + #include "rewrite/rewriteHandler.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" +@@ -161,6 +165,12 @@ typedef struct CopyStateData + char *raw_buf; + int raw_buf_index; /* next byte to process */ + int raw_buf_len; /* total # of bytes stored */ ++ ++ /* ++ * Dump/Restore support for security_label ++ */ ++ FmgrInfo seclabel_out_function; ++ bool seclabel_force_quot; + } CopyStateData; + + typedef CopyStateData *CopyState; +@@ -244,7 +254,7 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0"; + /* non-export function prototypes */ + static void DoCopyTo(CopyState cstate); + static void CopyTo(CopyState cstate); +-static void CopyOneRowTo(CopyState cstate, Oid tupleOid, ++static void CopyOneRowTo(CopyState cstate, HeapTuple tuple, + Datum *values, bool *nulls); + static void CopyFrom(CopyState cstate); + static bool CopyReadLine(CopyState cstate); +@@ -988,6 +998,10 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + + if (stmt->relation) + { ++ Bitmapset *columnsSet = NULL; ++ List *attnums; ++ ListCell *cur; ++ + Assert(!stmt->query); + cstate->queryDesc = NULL; + +@@ -998,16 +1012,20 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + tupDesc = RelationGetDescr(cstate->rel); + + /* Check relation permissions. */ ++ attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); ++ foreach(cur, attnums) ++ { ++ int index = lfirst_int(cur) ++ - FirstLowInvalidHeapAttributeNumber; ++ columnsSet = bms_add_member(columnsSet, index); ++ } ++ + relPerms = pg_class_aclmask(RelationGetRelid(cstate->rel), GetUserId(), + required_access, ACLMASK_ALL); + remainingPerms = required_access & ~relPerms; + if (remainingPerms != 0) + { + /* We don't have table permissions, check per-column permissions */ +- List *attnums; +- ListCell *cur; +- +- attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); + foreach(cur, attnums) + { + int attnum = lfirst_int(cur); +@@ -1021,6 +1039,13 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + } + } + ++ if (is_from) ++ sepgsql_relation_perms(RelationGetRelid(cstate->rel), ++ ACL_INSERT, NULL, columnsSet, true); ++ else ++ sepgsql_relation_perms(RelationGetRelid(cstate->rel), ++ ACL_SELECT, columnsSet, NULL, true); ++ + /* check read-only transaction */ + if (XactReadOnly && is_from && !cstate->rel->rd_islocaltemp) + PreventCommandIfReadOnly("COPY FROM"); +@@ -1130,11 +1155,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + int attnum = lfirst_int(cur); + + if (!list_member_int(cstate->attnumlist, attnum)) ++ { ++ Form_pg_attribute attForm; ++ ++ if (attnum > 0) ++ attForm = tupDesc->attrs[attnum - 1]; ++ else ++ attForm = SystemAttributeDefinition(attnum, true, true); ++ + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", +- NameStr(tupDesc->attrs[attnum - 1]->attname)))); +- cstate->force_quote_flags[attnum - 1] = true; ++ NameStr(attForm->attname)))); ++ } ++ ++ if (attnum == SecurityLabelAttributeNumber) ++ cstate->seclabel_force_quot = true; ++ else ++ cstate->force_quote_flags[attnum - 1] = true; + } + } + +@@ -1152,10 +1190,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + int attnum = lfirst_int(cur); + + if (!list_member_int(cstate->attnumlist, attnum)) ++ { ++ Form_pg_attribute attForm; ++ ++ if (attnum > 0) ++ attForm = tupDesc->attrs[attnum - 1]; ++ else ++ attForm = SystemAttributeDefinition(attnum, ++ tupDesc->tdhasoid, ++ tupDesc->tdhassecid); ++ + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", +- NameStr(tupDesc->attrs[attnum - 1]->attname)))); ++ NameStr(attForm->attname)))); ++ } ++ /* ignore system columns, if specified */ ++ if (attnum <= 0) ++ continue; + cstate->force_notnull_flags[attnum - 1] = true; + } + } +@@ -1347,16 +1399,29 @@ CopyTo(CopyState cstate) + int attnum = lfirst_int(cur); + Oid out_func_oid; + bool isvarlena; ++ FmgrInfo *out_fmgr; ++ Form_pg_attribute attForm; ++ ++ if (attnum == SecurityLabelAttributeNumber) ++ { ++ attForm = SystemAttributeDefinition(attnum, true, true); ++ out_fmgr = &cstate->seclabel_out_function; ++ } ++ else ++ { ++ attForm = attr[attnum - 1]; ++ out_fmgr = &cstate->out_functions[attnum - 1]; ++ } + + if (cstate->binary) +- getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, ++ getTypeBinaryOutputInfo(attForm->atttypid, + &out_func_oid, + &isvarlena); + else +- getTypeOutputInfo(attr[attnum - 1]->atttypid, ++ getTypeOutputInfo(attForm->atttypid, + &out_func_oid, + &isvarlena); +- fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); ++ fmgr_info(out_func_oid, out_fmgr); + } + + /* +@@ -1411,7 +1476,17 @@ CopyTo(CopyState cstate) + CopySendChar(cstate, cstate->delim[0]); + hdr_delim = true; + +- colname = NameStr(attr[attnum - 1]->attname); ++ if (SystemAttributeWritable(attnum, ++ tupDesc->tdhasoid, ++ tupDesc->tdhassecid)) ++ { ++ Form_pg_attribute attForm ++ = SystemAttributeDefinition(attnum, true, true); ++ ++ colname = NameStr(attForm->attname); ++ } ++ else ++ colname = NameStr(attr[attnum - 1]->attname); + + CopyAttributeOutCSV(cstate, colname, false, + list_length(cstate->attnumlist) == 1); +@@ -1441,7 +1516,7 @@ CopyTo(CopyState cstate) + heap_deform_tuple(tuple, tupDesc, values, nulls); + + /* Format and send the data */ +- CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls); ++ CopyOneRowTo(cstate, tuple, values, nulls); + } + + heap_endscan(scandesc); +@@ -1467,13 +1542,17 @@ CopyTo(CopyState cstate) + * Emit one row during CopyTo(). + */ + static void +-CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) ++CopyOneRowTo(CopyState cstate, HeapTuple tuple, Datum *values, bool *nulls) + { + bool need_delim = false; + FmgrInfo *out_functions = cstate->out_functions; + MemoryContext oldcontext; + ListCell *cur; + char *string; ++ Oid tupleOid = InvalidOid; ++ ++ if (HeapTupleIsValid(tuple)) ++ tupleOid = HeapTupleGetOid(tuple); + + MemoryContextReset(cstate->rowcontext); + oldcontext = MemoryContextSwitchTo(cstate->rowcontext); +@@ -1506,8 +1585,10 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); +- Datum value = values[attnum - 1]; +- bool isnull = nulls[attnum - 1]; ++ Datum value; ++ bool isnull; ++ bool force_quot; ++ FmgrInfo *out_fmgr; + + if (!cstate->binary) + { +@@ -1516,6 +1597,21 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + need_delim = true; + } + ++ if (attnum == SecurityLabelAttributeNumber) ++ { ++ value = seclabelSysattOutput(RelationGetRelid(cstate->rel), tuple); ++ isnull = false; ++ force_quot = cstate->seclabel_force_quot; ++ out_fmgr = &cstate->seclabel_out_function; ++ } ++ else ++ { ++ value = values[attnum - 1]; ++ isnull = nulls[attnum - 1]; ++ force_quot = cstate->force_quote_flags[attnum - 1]; ++ out_fmgr = &out_functions[attnum - 1]; ++ } ++ + if (isnull) + { + if (!cstate->binary) +@@ -1527,11 +1623,9 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + { + if (!cstate->binary) + { +- string = OutputFunctionCall(&out_functions[attnum - 1], +- value); ++ string = OutputFunctionCall(out_fmgr, value); + if (cstate->csv_mode) +- CopyAttributeOutCSV(cstate, string, +- cstate->force_quote_flags[attnum - 1], ++ CopyAttributeOutCSV(cstate, string, force_quot, + list_length(cstate->attnumlist) == 1); + else + CopyAttributeOutText(cstate, string); +@@ -1540,8 +1634,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + { + bytea *outputbytes; + +- outputbytes = SendFunctionCall(&out_functions[attnum - 1], +- value); ++ outputbytes = SendFunctionCall(out_fmgr, value); + CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); + CopySendData(cstate, VARDATA(outputbytes), + VARSIZE(outputbytes) - VARHDRSZ); +@@ -1675,8 +1768,10 @@ CopyFrom(CopyState cstate) + num_defaults; + FmgrInfo *in_functions; + FmgrInfo oid_in_function; ++ FmgrInfo seclabel_in_function; + Oid *typioparams; + Oid oid_typioparam; ++ Oid seclabel_typioparam; + int attnum; + int i; + Oid in_func_oid; +@@ -1919,6 +2014,19 @@ CopyFrom(CopyState cstate) + fmgr_info(in_func_oid, &oid_in_function); + } + ++ if (list_member_int(cstate->attnumlist, SecurityLabelAttributeNumber)) ++ { ++ if (!cstate->binary) ++ getTypeInputInfo(TEXTOID, &in_func_oid, &seclabel_typioparam); ++ else ++ getTypeBinaryInputInfo(TEXTOID, &in_func_oid, &seclabel_typioparam); ++ ++ fmgr_info(in_func_oid, &seclabel_in_function); ++ } ++ ++ ++ ++ + values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); + nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); + +@@ -1953,6 +2061,7 @@ CopyFrom(CopyState cstate) + { + bool skip_tuple; + Oid loaded_oid = InvalidOid; ++ Oid loaded_secid = InvalidOid; + + CHECK_FOR_INTERRUPTS(); + +@@ -2024,14 +2133,20 @@ CopyFrom(CopyState cstate) + /* Loop to read the user attributes on the line. */ + foreach(cur, cstate->attnumlist) + { ++ Form_pg_attribute attForm; + int attnum = lfirst_int(cur); + int m = attnum - 1; + ++ if (attnum == SecurityLabelAttributeNumber) ++ attForm = SystemAttributeDefinition(attnum, true, true); ++ else ++ attForm = attr[m]; ++ + if (fieldno >= fldct) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("missing data for column \"%s\"", +- NameStr(attr[m]->attname)))); ++ NameStr(attForm->attname)))); + string = field_strings[fieldno++]; + + if (cstate->csv_mode && string == NULL && +@@ -2041,14 +2156,38 @@ CopyFrom(CopyState cstate) + string = cstate->null_print; + } + +- cstate->cur_attname = NameStr(attr[m]->attname); ++ cstate->cur_attname = NameStr(attForm->attname); + cstate->cur_attval = string; +- values[m] = InputFunctionCall(&in_functions[m], +- string, +- typioparams[m], +- attr[m]->atttypmod); +- if (string != NULL) +- nulls[m] = false; ++ ++ if (attnum == SecurityLabelAttributeNumber) ++ { ++ if (string && !ignore_security_label_input) ++ { ++ Datum datum = InputFunctionCall(&seclabel_in_function, ++ string, ++ seclabel_typioparam, ++ attForm->atttypmod); ++ loaded_secid ++ = seclabelTransInput(RelationGetRelid(cstate->rel), ++ TextDatumGetCString(datum)); ++ } ++ } ++ else ++ { ++ if (cstate->csv_mode && string == NULL && ++ cstate->force_notnull_flags[m]) ++ { ++ /* Go ahead and read the NULL string */ ++ string = cstate->null_print; ++ } ++ ++ values[m] = InputFunctionCall(&in_functions[m], ++ string, ++ typioparams[m], ++ attForm->atttypmod); ++ if (string != NULL) ++ nulls[m] = false; ++ } + cstate->cur_attname = NULL; + cstate->cur_attval = NULL; + } +@@ -2094,17 +2233,38 @@ CopyFrom(CopyState cstate) + i = 0; + foreach(cur, cstate->attnumlist) + { ++ Form_pg_attribute attForm; + int attnum = lfirst_int(cur); + int m = attnum - 1; + +- cstate->cur_attname = NameStr(attr[m]->attname); ++ if (attnum == SecurityLabelAttributeNumber) ++ attForm = SystemAttributeDefinition(attnum, true, true); ++ else ++ attForm = attr[m]; ++ ++ cstate->cur_attname = NameStr(attForm->attname); + i++; +- values[m] = CopyReadBinaryAttribute(cstate, +- i, +- &in_functions[m], +- typioparams[m], +- attr[m]->atttypmod, +- &nulls[m]); ++ ++ if (attnum == SecurityLabelAttributeNumber) ++ { ++ Datum datum = CopyReadBinaryAttribute(cstate, i, ++ &seclabel_in_function, ++ seclabel_typioparam, ++ attForm->atttypmod, ++ &isnull); ++ if (!isnull && !ignore_security_label_input) ++ loaded_secid ++ = seclabelTransInput(RelationGetRelid(cstate->rel), ++ TextDatumGetCString(datum)); ++ } ++ else ++ { ++ values[m] = CopyReadBinaryAttribute(cstate, i, ++ &in_functions[m], ++ typioparams[m], ++ attr[m]->atttypmod, ++ &nulls[m]); ++ } + cstate->cur_attname = NULL; + } + } +@@ -2125,6 +2285,8 @@ CopyFrom(CopyState cstate) + + if (cstate->oids && file_has_oids) + HeapTupleSetOid(tuple, loaded_oid); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, loaded_secid); + + /* Triggers and stuff need to be invoked in query context. */ + MemoryContextSwitchTo(oldcontext); +@@ -2149,6 +2311,9 @@ CopyFrom(CopyState cstate) + } + + if (!skip_tuple) ++ sepgsql_tuple_insert(cstate->rel, tuple); ++ ++ if (!skip_tuple) + { + List *recheckIndexes = NIL; + +@@ -3442,6 +3607,17 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) + } + if (attnum == InvalidAttrNumber) + { ++ Form_pg_attribute attForm; ++ bool hasoid = tupDesc->tdhasoid; ++ bool hassecid = tupDesc->tdhassecid; ++ ++ attForm = SystemAttributeByName(name, hasoid, hassecid); ++ if (attForm && ++ SystemAttributeWritable(attForm->attnum, hasoid, hassecid)) ++ attnum = attForm->attnum; ++ } ++ if (attnum == InvalidAttrNumber) ++ { + if (rel != NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), +@@ -3489,7 +3665,7 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self) + slot_getallattrs(slot); + + /* And send the data */ +- CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); ++ CopyOneRowTo(cstate, slot->tts_tuple, slot->tts_values, slot->tts_isnull); + } + + /* +diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c +index e7dac22..16360db 100644 +--- a/src/backend/commands/dbcommands.c ++++ b/src/backend/commands/dbcommands.c +@@ -35,6 +35,8 @@ + #include "catalog/pg_authid.h" + #include "catalog/pg_database.h" + #include "catalog/pg_db_role_setting.h" ++#include "catalog/pg_seclabel.h" ++#include "catalog/pg_shdescription.h" + #include "catalog/pg_tablespace.h" + #include "commands/comment.h" + #include "commands/dbcommands.h" +@@ -43,6 +45,7 @@ + #include "miscadmin.h" + #include "pgstat.h" + #include "postmaster/bgwriter.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "storage/lmgr.h" +@@ -111,6 +114,7 @@ createdb(const CreatedbStmt *stmt) + Datum new_record[Natts_pg_database]; + bool new_record_nulls[Natts_pg_database]; + Oid dboid; ++ Oid dbsecid; + Oid datdba; + ListCell *option; + DefElem *dtablespacename = NULL; +@@ -486,6 +490,9 @@ createdb(const CreatedbStmt *stmt) + /* Note there is no additional permission check in this path */ + } + ++ /* SELinux permission checks */ ++ dbsecid = sepgsql_database_create(dbname, src_dboid); ++ + /* + * Check for db name conflict. This is just to give a more friendly error + * message than "unique index violation". There's a race condition but +@@ -560,6 +567,8 @@ createdb(const CreatedbStmt *stmt) + new_record, new_record_nulls); + + HeapTupleSetOid(tuple, dboid); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, dbsecid); + + simple_heap_insert(pg_database_rel, tuple); + +@@ -576,6 +585,9 @@ createdb(const CreatedbStmt *stmt) + /* Create pg_shdepend entries for objects within database */ + copyTemplateDependencies(src_dboid, dboid); + ++ /* Create pg_seclabel entries for objects within database */ ++ seclabelOnCreateDatabase(src_dboid, dboid); ++ + /* + * Force a checkpoint before starting the copy. This will force dirty + * buffers out to disk, to ensure source database is up-to-date on disk +@@ -777,6 +789,9 @@ dropdb(const char *dbname, bool missing_ok) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + dbname); + ++ /* SELinux checks */ ++ sepgsql_database_drop(db_id, false); ++ + /* + * Disallow dropping a DB that is marked istemplate. This is just to + * prevent people from accidentally dropping template0 or template1; they +@@ -833,6 +848,11 @@ dropdb(const char *dbname, bool missing_ok) + dropDatabaseDependencies(db_id); + + /* ++ * Remove pg_seclabel entries for the database ++ */ ++ seclabelOnDropDatabase(db_id); ++ ++ /* + * Drop pages for this database that are in the shared buffer cache. This + * is important to ensure that no remaining backend tries to write out a + * dirty buffer to the dead database later... +@@ -915,6 +935,9 @@ RenameDatabase(const char *oldname, const char *newname) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to rename database"))); + ++ /* SELinux checks */ ++ sepgsql_database_alter(db_id); ++ + /* + * Make sure the new name doesn't exist. See notes for same error in + * CREATE DATABASE. +@@ -1053,6 +1076,9 @@ movedb(const char *dbname, const char *tblspcname) + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("pg_global cannot be used as default tablespace"))); + ++ /* SELinux checks */ ++ sepgsql_database_alter(db_id); ++ + /* + * No-op if same tablespace + */ +@@ -1369,6 +1395,9 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + stmt->dbname); + ++ /* SELinux checks */ ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); ++ + /* + * Build an updated tuple, perusing the information just obtained + */ +@@ -1419,6 +1448,9 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + stmt->dbname); + ++ /* SELinux checks */ ++ sepgsql_database_alter(datid); ++ + AlterSetting(datid, InvalidOid, stmt->setstmt); + + UnlockSharedObject(DatabaseRelationId, datid, 0, AccessShareLock); +@@ -1494,6 +1526,9 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to change owner of database"))); + ++ /* SELinux checks */ ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); ++ + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + +@@ -1533,6 +1568,58 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) + heap_close(rel, NoLock); + } + ++/* ++ * ALTER DATABASE SECURITY LABEL TO ++ */ ++void ++AlterDatabaseSecLabel(const char *dbname, char *new_label) ++{ ++ Relation rel; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ Oid databaseId; ++ Oid securityId; ++ ++ /* Fetch the old tuple */ ++ rel = heap_open(DatabaseRelationId, RowExclusiveLock); ++ ScanKeyInit(&skey, ++ Anum_pg_database_datname, ++ BTEqualStrategyNumber, F_NAMEEQ, ++ NameGetDatum(dbname)); ++ scan = systable_beginscan(rel, DatabaseNameIndexId, true, ++ SnapshotNow, 1, &skey); ++ oldtup = systable_getnext(scan); ++ if (!HeapTupleIsValid(oldtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_DATABASE), ++ errmsg("database \"%s\" does not exist", dbname))); ++ ++ newtup = heap_copytuple(oldtup); ++ ++ systable_endscan(scan); ++ ++ databaseId = HeapTupleGetOid(newtup); ++ ++ /* DAC permission checks */ ++ if (!pg_database_ownercheck(databaseId, GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); ++ ++ /* SELinux checks */ ++ securityId = sepgsql_database_relabel(databaseId, new_label); ++ ++ /* Update it */ ++ HeapTupleSetSecid(newtup, securityId); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ ++ heap_close(rel, RowExclusiveLock); ++} + + /* + * Helper functions +diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c +index 0bda24a..334b123 100644 +--- a/src/backend/commands/explain.c ++++ b/src/backend/commands/explain.c +@@ -257,7 +257,7 @@ ExplainResultDesc(ExplainStmt *stmt) + } + + /* Need a tuple descriptor representing a single TEXT or XML column */ +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN", + xml ? XMLOID : TEXTOID, -1, 0); + return tupdesc; +diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c +index 14356a2..d1f255e 100644 +--- a/src/backend/commands/foreigncmds.c ++++ b/src/backend/commands/foreigncmds.c +@@ -27,6 +27,7 @@ + #include "foreign/foreign.h" + #include "miscadmin.h" + #include "parser/parse_func.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -230,6 +231,9 @@ AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId) + fdwId = HeapTupleGetOid(tup); + form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup); + ++ /* SELinux checks */ ++ sepgsql_fdw_alter(fdwId, InvalidOid); ++ + if (form->fdwowner != newOwnerId) + { + form->fdwowner = newOwnerId; +@@ -294,6 +298,8 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + } + } ++ /* SELinux checks */ ++ sepgsql_fserver_alter(srvId); + + form->srvowner = newOwnerId; + +@@ -339,6 +345,7 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) + Oid fdwvalidator; + Datum fdwoptions; + Oid ownerId; ++ Oid securityId; + + /* Must be super user */ + if (!superuser()) +@@ -391,8 +398,13 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) + else + nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; + ++ /* SELinux checks */ ++ securityId = sepgsql_fdw_create(stmt->fdwname, fdwvalidator); ++ + tuple = heap_form_tuple(rel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tuple, securityId); ++ + fdwId = simple_heap_insert(rel, tuple); + CatalogUpdateIndexes(rel, tuple); + +@@ -511,6 +523,8 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt) + + repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; + } ++ /* SELinux checks */ ++ sepgsql_fdw_alter(fdwId, fdwvalidator); + + /* Everything looks good - update the tuple */ + +@@ -559,6 +573,8 @@ RemoveForeignDataWrapper(DropFdwStmt *stmt) + stmt->fdwname))); + return; + } ++ /* SELinux checks */ ++ sepgsql_fdw_drop(fdwId, false); + + /* + * Do the deletion +@@ -608,6 +624,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt) + HeapTuple tuple; + Oid srvId; + Oid ownerId; ++ Oid securityId; + AclResult aclresult; + ObjectAddress myself; + ObjectAddress referenced; +@@ -635,6 +652,9 @@ CreateForeignServer(CreateForeignServerStmt *stmt) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + ++ /* SELinux checks */ ++ securityId = sepgsql_fserver_create(stmt->servername, fdw->fdwid); ++ + /* + * Insert tuple into pg_foreign_server. + */ +@@ -678,6 +698,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt) + + tuple = heap_form_tuple(rel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tuple, securityId); ++ + srvId = simple_heap_insert(rel, tuple); + + CatalogUpdateIndexes(rel, tuple); +@@ -732,6 +754,9 @@ AlterForeignServer(AlterForeignServerStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, + stmt->servername); + ++ /* SELinux checks */ ++ sepgsql_fserver_alter(srvId); ++ + memset(repl_val, 0, sizeof(repl_val)); + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); +@@ -823,6 +848,8 @@ RemoveForeignServer(DropForeignServerStmt *stmt) + if (!pg_foreign_server_ownercheck(srvId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, + stmt->servername); ++ /* SELinux checks */ ++ sepgsql_fserver_drop(srvId, false); + + object.classId = ForeignServerRelationId; + object.objectId = srvId; +@@ -896,6 +923,7 @@ CreateUserMapping(CreateUserMappingStmt *stmt) + HeapTuple tuple; + Oid useId; + Oid umId; ++ Oid securityId; + ObjectAddress myself; + ObjectAddress referenced; + ForeignServer *srv; +@@ -908,6 +936,9 @@ CreateUserMapping(CreateUserMappingStmt *stmt) + + user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername); + ++ /* SELinux checks */ ++ securityId = sepgsql_user_mapping_create(useId, srv->serverid); ++ + /* + * Check that the user mapping is unique within server. + */ +@@ -947,6 +978,8 @@ CreateUserMapping(CreateUserMappingStmt *stmt) + + tuple = heap_form_tuple(rel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tuple, securityId); ++ + umId = simple_heap_insert(rel, tuple); + + CatalogUpdateIndexes(rel, tuple); +@@ -1000,6 +1033,9 @@ AlterUserMapping(AlterUserMappingStmt *stmt) + + user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername); + ++ /* SELinux checks */ ++ sepgsql_user_mapping_alter(umId); ++ + tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId)); + + if (!HeapTupleIsValid(tp)) +@@ -1114,6 +1150,9 @@ RemoveUserMapping(DropUserMappingStmt *stmt) + + user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername); + ++ /* SELinux checks */ ++ sepgsql_user_mapping_drop(umId, false); ++ + /* + * Do the deletion + */ +diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c +index 9a584ed..f914280 100644 +--- a/src/backend/commands/functioncmds.c ++++ b/src/backend/commands/functioncmds.c +@@ -43,6 +43,7 @@ + #include "catalog/pg_namespace.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_proc_fn.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_type.h" + #include "catalog/pg_type_fn.h" + #include "commands/defrem.h" +@@ -53,6 +54,7 @@ + #include "parser/parse_expr.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -788,6 +790,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) + ArrayType *parameterNames; + List *parameterDefaults; + Oid requiredResultType; ++ Oid replacedFunc = InvalidOid; ++ Oid prosecid; + bool isWindowFunc, + isStrict, + security; +@@ -933,6 +937,18 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) + errmsg("ROWS is not applicable when function does not return a set"))); + + /* ++ * SELinux checks ++ */ ++ if (stmt->replace) ++ replacedFunc = GetSysCacheOid3(PROCNAMEARGSNSP, ++ PointerGetDatum(funcname), ++ PointerGetDatum(parameterTypes), ++ ObjectIdGetDatum(namespaceId)); ++ ++ prosecid = sepgsql_proc_create(funcname, replacedFunc, ++ namespaceId, languageOid); ++ ++ /* + * And now that we have all the parameters, and know we're permitted to do + * so, go ahead and create the function. + */ +@@ -957,7 +973,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) + parameterDefaults, + PointerGetDatum(proconfig), + procost, +- prorows); ++ prorows, ++ prosecid); + } + + +@@ -999,6 +1016,9 @@ RemoveFunction(RemoveFuncStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(functionName)); + ++ /* SELinux checks */ ++ sepgsql_proc_drop(funcOid, false); ++ + if (((Form_pg_proc) GETSTRUCT(tup))->proisagg) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), +@@ -1135,6 +1155,9 @@ RenameFunction(List *name, List *argtypes, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_proc_alter_rename(procOid, newname); ++ + /* rename */ + namestrcpy(&(procForm->proname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -1239,6 +1262,8 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(procForm->pronamespace)); + } ++ /* SELinux checks */ ++ sepgsql_proc_alter(procOid); + + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); +@@ -1277,6 +1302,49 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) + } + + /* ++ * ALTER FUNCTION f(...) SECURITY LABEL TO ++ */ ++void ++AlterFunctionSecLabel(List *name, List *argtypes, bool isagg, char *new_label) ++{ ++ Relation rel; ++ HeapTuple tuple; ++ Oid procOid; ++ Oid securityId; ++ ++ /* open pg_proc system catalog */ ++ rel = heap_open(ProcedureRelationId, RowExclusiveLock); ++ ++ /* get function OID */ ++ if (isagg) ++ procOid = LookupAggNameTypeNames(name, argtypes, false); ++ else ++ procOid = LookupFuncNameTypeNames(name, argtypes, false); ++ ++ tuple = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for function %u", procOid); ++ ++ /* Must be owner */ ++ if (!pg_proc_ownercheck(procOid, GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, ++ get_func_name(procOid)); ++ ++ /* SELinux checks */ ++ securityId = sepgsql_proc_relabel(procOid, new_label); ++ ++ /* Update it */ ++ HeapTupleSetSecid(tuple, securityId); ++ ++ simple_heap_update(rel, &tuple->t_self, tuple); ++ CatalogUpdateIndexes(rel, tuple); ++ ++ heap_freetuple(tuple); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++/* + * Implements the ALTER FUNCTION utility command (except for the + * RENAME and OWNER clauses, which are handled as part of the generic + * ALTER framework). +@@ -1313,6 +1381,9 @@ AlterFunction(AlterFunctionStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(stmt->func->funcname)); + ++ /* SELinux checks */ ++ sepgsql_proc_alter(funcOid); ++ + if (procForm->proisagg) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), +@@ -1659,6 +1730,9 @@ CreateCast(CreateCastStmt *stmt) + errmsg("array data types are not binary-compatible"))); + } + ++ /* SELinux checks */ ++ sepgsql_cast_create(sourcetypeid, targettypeid, castmethod, funcid); ++ + /* + * Allow source and target types to be same only for length coercion + * functions. We assume a multi-arg function does length coercion. +@@ -1795,6 +1869,9 @@ DropCast(DropCastStmt *stmt) + format_type_be(sourcetypeid), + format_type_be(targettypeid)))); + ++ /* SELinux checks */ ++ sepgsql_cast_drop(sourcetypeid, targettypeid, false); ++ + /* + * Do the deletion + */ +@@ -1873,6 +1950,9 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, + /* get schema OID and check its permissions */ + nspOid = LookupCreationNamespace(newschema); + ++ /* SELinux checks */ ++ sepgsql_proc_alter_schema(procOid, nspOid); ++ + if (oldNspOid == nspOid) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_FUNCTION), +diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c +index 94657b8..7cb3634 100644 +--- a/src/backend/commands/indexcmds.c ++++ b/src/backend/commands/indexcmds.c +@@ -39,6 +39,7 @@ + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parsetree.h" ++#include "sepgsql/hooks.h" + #include "storage/lmgr.h" + #include "storage/proc.h" + #include "storage/procarray.h" +@@ -242,6 +243,10 @@ DefineIndex(RangeVar *heapRelation, + get_tablespace_name(tablespaceId)); + } + ++ /* SELinux checks */ ++ if (check_rights) ++ sepgsql_index_create(relationId, namespaceId); ++ + /* + * Force shared indexes into the pg_global tablespace. This is a bit of a + * hack but seems simpler than marking them in the BKI commands. On the +@@ -363,7 +368,9 @@ DefineIndex(RangeVar *heapRelation, + errmsg("primary keys cannot be expressions"))); + + /* System attributes are never null, so no problem */ +- if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids)) ++ if (SystemAttributeByName(key->name, ++ rel->rd_rel->relhasoids, ++ rel->rd_rel->relhassecids)) + continue; + + atttuple = SearchSysCacheAttName(relationId, key->name); +@@ -1572,6 +1579,9 @@ ReindexIndex(RangeVar *indexRelation) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + indexRelation->relname); + ++ /* SELinux checks */ ++ sepgsql_index_reindex(indOid); ++ + ReleaseSysCache(tuple); + + reindex_index(indOid, false); +@@ -1604,6 +1614,9 @@ ReindexTable(RangeVar *relation) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + relation->relname); + ++ /* SELinux checks */ ++ sepgsql_relation_reindex(heapOid); ++ + ReleaseSysCache(tuple); + + if (!reindex_relation(heapOid, true, false)) +@@ -1642,6 +1655,9 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + databaseName); + ++ /* SELinux checks */ ++ sepgsql_database_reindex(MyDatabaseId); ++ + /* + * Create a memory context that will survive forced transaction commits we + * do below. Since it is a child of PortalContext, it will go away +diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c +index 283947a..657ff95 100644 +--- a/src/backend/commands/lockcmds.c ++++ b/src/backend/commands/lockcmds.c +@@ -20,6 +20,7 @@ + #include "commands/lockcmds.h" + #include "miscadmin.h" + #include "parser/parse_clause.h" ++#include "sepgsql/hooks.h" + #include "storage/lmgr.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" +@@ -149,6 +150,9 @@ LockTableRecurse(Oid reloid, RangeVar *rv, + errmsg("\"%s\" is not a table", + RelationGetRelationName(rel)))); + ++ /* SELinux checks */ ++ sepgsql_relation_lock(rel); ++ + /* + * If requested, recurse to children. We use find_inheritance_children + * not find_all_inheritors to avoid taking locks far in advance of +diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c +index ac0270f..25b84fd 100644 +--- a/src/backend/commands/opclasscmds.c ++++ b/src/backend/commands/opclasscmds.c +@@ -35,6 +35,7 @@ + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -164,7 +165,8 @@ OpClassCacheLookup(Oid amID, List *opclassname) + * Caller must have done permissions checks etc. already. + */ + static Oid +-CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) ++CreateOpFamily(char *amname, char *opfname, ++ Oid namespaceoid, Oid amoid, Oid securityId) + { + Oid opfamilyoid; + Relation rel; +@@ -204,6 +206,8 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) + + tup = heap_form_tuple(rel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + opfamilyoid = simple_heap_insert(rel, tup); + + CatalogUpdateIndexes(rel, tup); +@@ -369,11 +373,16 @@ DefineOpClass(CreateOpClassStmt *stmt) + } + else + { ++ Oid securityId; ++ ++ /* SELinux checks */ ++ securityId = sepgsql_opfamily_create(opcname, namespaceoid, amoid); ++ + /* + * Create it ... again no need for more permissions ... + */ + opfamilyoid = CreateOpFamily(stmt->amname, opcname, +- namespaceoid, amoid); ++ namespaceoid, amoid, securityId); + } + } + +@@ -505,6 +514,12 @@ DefineOpClass(CreateOpClassStmt *stmt) + stmt->amname))); + } + ++ /* SELinux checks */ ++ sepgsql_opfamily_alter(opfamilyoid, false, amoid, ++ operators, procedures); ++ sepgsql_opclass_create(opcname, namespaceoid, ++ typeoid, opfamilyoid, storageoid); ++ + rel = heap_open(OperatorClassRelationId, RowExclusiveLock); + + /* +@@ -650,6 +665,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) + NameData opfName; + ObjectAddress myself, + referenced; ++ Oid securityId; + + /* Convert list of names to a name and namespace */ + namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname, +@@ -701,6 +717,9 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) + errmsg("operator family \"%s\" for access method \"%s\" already exists", + opfname, stmt->amname))); + ++ /* SELinux checks */ ++ securityId = sepgsql_opfamily_create(opfname, namespaceoid, amoid); ++ + /* + * Okay, let's create the pg_opfamily entry. + */ +@@ -715,6 +734,8 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) + + tup = heap_form_tuple(rel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + opfamilyoid = simple_heap_insert(rel, tup); + + CatalogUpdateIndexes(rel, tup); +@@ -925,6 +946,9 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid, + break; + } + } ++ /* SELinux checks */ ++ sepgsql_opfamily_alter(opfamilyoid, false, amoid, ++ operators, procedures); + + /* + * Add tuples to pg_amop and pg_amproc tying in the operators and +@@ -1002,6 +1026,9 @@ AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid, + } + } + ++ /* SELinux checks */ ++ sepgsql_opfamily_alter(opfamilyoid, true, amoid, operators, procedures); ++ + /* + * Remove tuples from pg_amop and pg_amproc. + */ +@@ -1522,6 +1549,9 @@ RemoveOpClass(RemoveOpClassStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, + NameListToString(stmt->opclassname)); + ++ /* SELinux checks */ ++ sepgsql_opclass_drop(opcID, false); ++ + ReleaseSysCache(tuple); + + /* +@@ -1583,6 +1613,9 @@ RemoveOpFamily(RemoveOpFamilyStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, + NameListToString(stmt->opfamilyname)); + ++ /* SELinux checks */ ++ sepgsql_opfamily_drop(opfID, false); ++ + ReleaseSysCache(tuple); + + /* +@@ -1781,6 +1814,9 @@ RenameOpClass(List *name, const char *access_method, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_opclass_alter_rename(opcOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -1875,6 +1911,9 @@ RenameOpFamily(List *name, const char *access_method, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_opfamily_alter_rename(opfOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -1990,6 +2029,8 @@ AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_opclass_alter(HeapTupleGetOid(tup)); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +@@ -2112,7 +2153,8 @@ AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } +- ++ /* SELinux checks */ ++ sepgsql_opfamily_alter_owner(HeapTupleGetOid(tup), newOwnerId); + /* + * Modify the owner --- okay to scribble on tup because it's a copy + */ +diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c +index 9c07cf3..2e19448 100644 +--- a/src/backend/commands/operatorcmds.c ++++ b/src/backend/commands/operatorcmds.c +@@ -45,6 +45,7 @@ + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + #include "utils/rel.h" +@@ -319,6 +320,9 @@ RemoveOperator(RemoveFuncStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, + NameListToString(operatorName)); + ++ /* SELinux checks */ ++ sepgsql_operator_drop(operOid, false); ++ + ReleaseSysCache(tup); + + /* +@@ -426,6 +430,8 @@ AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(oprForm->oprnamespace)); + } ++ /* SELinux checks */ ++ sepgsql_operator_alter(operOid); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c +index e765382..a54dd3f 100644 +--- a/src/backend/commands/prepare.c ++++ b/src/backend/commands/prepare.c +@@ -759,7 +759,7 @@ pg_prepared_statement(PG_FUNCTION_ARGS) + * build tupdesc for result tuples. This must match the definition of the + * pg_prepared_statements view in system_views.sql + */ +- tupdesc = CreateTemplateTupleDesc(5, false); ++ tupdesc = CreateTemplateTupleDesc(5, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", +diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c +index 633a093..a3375b2 100644 +--- a/src/backend/commands/proclang.c ++++ b/src/backend/commands/proclang.c +@@ -29,6 +29,7 @@ + #include "miscadmin.h" + #include "parser/parse_func.h" + #include "parser/parser.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -145,7 +146,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) + NIL, + PointerGetDatum(NULL), + 1, +- 0); ++ 0, ++ InvalidOid); + } + + /* +@@ -180,7 +182,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) + NIL, + PointerGetDatum(NULL), + 1, +- 0); ++ 0, ++ InvalidOid); + } + } + else +@@ -218,7 +221,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) + NIL, + PointerGetDatum(NULL), + 1, +- 0); ++ 0, ++ InvalidOid); + } + } + else +diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c +index b0a9a22..57951fe 100644 +--- a/src/backend/commands/schemacmds.c ++++ b/src/backend/commands/schemacmds.c +@@ -21,10 +21,12 @@ + #include "catalog/indexing.h" + #include "catalog/namespace.h" + #include "catalog/pg_namespace.h" ++#include "catalog/pg_seclabel.h" + #include "commands/dbcommands.h" + #include "commands/schemacmds.h" + #include "miscadmin.h" + #include "parser/parse_utilcmd.h" ++#include "sepgsql/hooks.h" + #include "tcop/utility.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -49,6 +51,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) + Oid owner_uid; + Oid saved_uid; + int save_sec_context; ++ Oid secid; + AclResult aclresult; + + GetUserIdAndSecContext(&saved_uid, &save_sec_context); +@@ -75,6 +78,9 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) + + check_is_member_of_role(saved_uid, owner_uid); + ++ /* SELinux checks */ ++ secid = sepgsql_schema_create(schemaName, false); ++ + /* Additional check to protect reserved schema names */ + if (!allowSystemTableMods && IsReservedName(schemaName)) + ereport(ERROR, +@@ -95,7 +101,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); + + /* Create the schema's namespace */ +- namespaceId = NamespaceCreate(schemaName, owner_uid); ++ namespaceId = NamespaceCreate(schemaName, owner_uid, secid); + + /* Advance cmd counter to make the namespace visible */ + CommandCounterIncrement(); +@@ -204,6 +210,9 @@ RemoveSchemas(DropStmt *drop) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, + namespaceName); + ++ /* SELinux checks */ ++ sepgsql_schema_drop(namespaceId, false); ++ + object.classId = NamespaceRelationId; + object.objectId = namespaceId; + object.objectSubId = 0; +@@ -288,6 +297,9 @@ RenameSchema(const char *oldname, const char *newname) + errmsg("unacceptable schema name \"%s\"", newname), + errdetail("The prefix \"pg_\" is reserved for system schemas."))); + ++ /* SELinux checks */ ++ sepgsql_schema_alter(HeapTupleGetOid(tup)); ++ + /* rename */ + namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -389,6 +401,9 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_DATABASE, + get_database_name(MyDatabaseId)); + ++ /* SELinux checks */ ++ sepgsql_schema_alter(HeapTupleGetOid(tup)); ++ + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + +@@ -423,3 +438,43 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) + } + + } ++ ++/* ++ * ALTER SCHEMA SECURITY LABEL TO ++ */ ++void ++AlterSchemaSecLabel(const char *name, char *new_label) ++{ ++ Relation rel; ++ HeapTuple tuple; ++ Oid namespaceId; ++ Oid securityId; ++ ++ /* open pg_namespace relation */ ++ rel = heap_open(NamespaceRelationId, RowExclusiveLock); ++ tuple = SearchSysCacheCopy1(NAMESPACENAME, ++ CStringGetDatum(name)); ++ if (!HeapTupleIsValid(tuple)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_SCHEMA), ++ errmsg("schema \"%s\" does not exist", name))); ++ namespaceId = HeapTupleGetOid(tuple); ++ ++ /* DAC permission check */ ++ if (!pg_namespace_ownercheck(namespaceId, GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, name); ++ ++ /* SELinux checks */ ++ securityId = sepgsql_schema_relabel(namespaceId, new_label); ++ ++ /* Update it */ ++ HeapTupleSetSecid(tuple, securityId); ++ ++ simple_heap_update(rel, &tuple->t_self, tuple); ++ ++ CatalogUpdateIndexes(rel, tuple); ++ ++ heap_freetuple(tuple); ++ ++ heap_close(rel, RowExclusiveLock); ++} +diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c +index f52e1d8..ecff05d 100644 +--- a/src/backend/commands/sequence.c ++++ b/src/backend/commands/sequence.c +@@ -26,6 +26,7 @@ + #include "commands/tablecmds.h" + #include "miscadmin.h" + #include "nodes/makefuncs.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/proc.h" +@@ -331,6 +332,9 @@ AlterSequence(AlterSeqStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + stmt->sequence->relname); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(relid); ++ + /* do the work */ + AlterSequenceInternal(relid, stmt->options); + } +@@ -469,6 +473,9 @@ nextval_internal(Oid relid) + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux checks */ ++ sepgsql_sequence_next_value(elm->relid); ++ + /* read-only transactions may only modify temp sequences */ + if (!seqrel->rd_islocaltemp) + PreventCommandIfReadOnly("nextval()"); +@@ -668,6 +675,9 @@ currval_oid(PG_FUNCTION_ARGS) + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux checks */ ++ sepgsql_sequence_get_value(elm->relid); ++ + if (!elm->last_valid) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), +@@ -710,6 +720,9 @@ lastval(PG_FUNCTION_ARGS) + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux checks */ ++ sepgsql_sequence_get_value(last_used_seq->relid); ++ + result = last_used_seq->last; + relation_close(seqrel, NoLock); + +@@ -746,6 +759,9 @@ do_setval(Oid relid, int64 next, bool iscalled) + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux checks */ ++ sepgsql_sequence_set_value(elm->relid); ++ + /* read-only transactions may only modify temp sequences */ + if (!seqrel->rd_islocaltemp) + PreventCommandIfReadOnly("setval()"); +diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c +index 25b2807..cbe483b 100644 +--- a/src/backend/commands/tablecmds.c ++++ b/src/backend/commands/tablecmds.c +@@ -32,6 +32,7 @@ + #include "catalog/pg_inherits_fn.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_opclass.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_trigger.h" + #include "catalog/pg_type.h" +@@ -62,12 +63,14 @@ + #include "parser/parser.h" + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteHandler.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/smgr.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" ++#include "utils/guc.h" + #include "utils/inval.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" +@@ -223,7 +226,7 @@ static const struct dropmsgstrings dropmsgstringarray[] = { + + static void truncate_check_rel(Relation rel); + static List *MergeAttributes(List *schema, List *supers, bool istemp, +- List **supOids, List **supconstr, int *supOidCount); ++ List **supOids, List **supconstr, int *supOidCount, int *supSecidCount); + static bool MergeCheckConstraint(List *constraints, char *name, Node *expr); + static bool change_varattnos_walker(Node *node, const AttrNumber *newattno); + static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel); +@@ -271,10 +274,12 @@ static void ATOneLevelRecursion(List **wqueue, Relation rel, + static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, + AlterTableCmd *cmd); + static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +- ColumnDef *colDef, bool isOid); ++ ColumnDef *colDef, bool isOid, bool isSecid); + static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid); + static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse, + AlterTableCmd *cmd); ++static void ATPrepAddSecLabel(List **wqueue, Relation rel, bool recurse, ++ AlterTableCmd *cmd); + static void ATExecDropNotNull(Relation rel, const char *colName); + static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, + const char *colName); +@@ -355,6 +360,7 @@ DefineRelation(CreateStmt *stmt, char relkind) + List *old_constraints; + bool localHasOids; + int parentOidCount; ++ int parentSecidCount; + List *rawDefaults; + List *cookedDefaults; + Datum reloptions; +@@ -362,6 +368,7 @@ DefineRelation(CreateStmt *stmt, char relkind) + AttrNumber attnum; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; + Oid ofTypeId; ++ Oid *secLabels; + + /* + * Truncate relname to appropriate length (probably a waste of time, as +@@ -461,7 +468,8 @@ DefineRelation(CreateStmt *stmt, char relkind) + */ + schema = MergeAttributes(schema, stmt->inhRelations, + stmt->relation->istemp, +- &inheritOids, &old_constraints, &parentOidCount); ++ &inheritOids, &old_constraints, ++ &parentOidCount, &parentSecidCount); + + /* + * Create a tuple descriptor from the relation schema. Note that this +@@ -473,6 +481,16 @@ DefineRelation(CreateStmt *stmt, char relkind) + localHasOids = interpretOidsOption(stmt->options); + descriptor->tdhasoid = (localHasOids || parentOidCount > 0); + ++ if ((relkind == RELKIND_RELATION && default_with_secids) || parentSecidCount > 0) ++ descriptor->tdhassecid = true; ++ ++ /* SELinux permission checks */ ++ secLabels = sepgsql_relation_create(relname, ++ relkind, ++ descriptor, ++ namespaceId, ++ inheritOids, ++ false); + /* + * Find columns with default values and prepare for insertion of the + * defaults. Pre-cooked (that is, inherited) defaults go into a list of +@@ -546,7 +564,8 @@ DefineRelation(CreateStmt *stmt, char relkind) + stmt->oncommit, + reloptions, + true, +- allowSystemTableMods); ++ allowSystemTableMods, ++ secLabels); + + StoreCatalogInheritance(relationId, inheritOids); + +@@ -755,6 +774,9 @@ RemoveRelations(DropStmt *drop) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + rel->relname); + ++ /* SELinux checks */ ++ sepgsql_relation_drop(relOid, false); ++ + if (!allowSystemTableMods && IsSystemClass(classform)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), +@@ -918,6 +940,9 @@ ExecuteTruncate(TruncateStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(seq_rel)); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(seq_relid); ++ + seq_relids = lappend_oid(seq_relids, seq_relid); + + relation_close(seq_rel, NoLock); +@@ -1086,6 +1111,8 @@ truncate_check_rel(Relation rel) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_relation_truncate(rel); + + if (!allowSystemTableMods && IsSystemRelation(rel)) + ereport(ERROR, +@@ -1146,6 +1173,7 @@ storage_name(char c) + * 'supconstr' receives a list of constraints belonging to the parents, + * updated as necessary to be valid for the child. + * 'supOidCount' is set to the number of parents that have OID columns. ++ * 'supSecidCount' is set to the number of parents that have SID columns. + * + * Return value: + * Completed schema list. +@@ -1191,13 +1219,15 @@ storage_name(char c) + */ + static List * + MergeAttributes(List *schema, List *supers, bool istemp, +- List **supOids, List **supconstr, int *supOidCount) ++ List **supOids, List **supconstr, ++ int *supOidCount, int *supSecidCount) + { + ListCell *entry; + List *inhSchema = NIL; + List *parentOids = NIL; + List *constraints = NIL; + int parentsWithOids = 0; ++ int parentsWithSecids = 0; + bool have_bogus_defaults = false; + int child_attno; + static Node bogus_marker = {0}; /* marks conflicting defaults */ +@@ -1325,6 +1355,8 @@ MergeAttributes(List *schema, List *supers, bool istemp, + + if (relation->rd_rel->relhasoids) + parentsWithOids++; ++ if (relation->rd_rel->relhassecids) ++ parentsWithSecids++; + + tupleDesc = RelationGetDescr(relation); + constr = tupleDesc->constr; +@@ -1626,6 +1658,7 @@ MergeAttributes(List *schema, List *supers, bool istemp, + *supOids = parentOids; + *supconstr = constraints; + *supOidCount = parentsWithOids; ++ *supSecidCount = parentsWithSecids; + return schema; + } + +@@ -1985,6 +2018,9 @@ renameatt(Oid myrelid, + errmsg("permission denied: \"%s\" is a system catalog", + RelationGetRelationName(targetrelation)))); + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(myrelid, oldattname); ++ + /* + * if the 'recurse' flag is set then we are supposed to rename this + * attribute in all classes that inherit from 'relname' (as well as in +@@ -2136,6 +2172,9 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype) + errmsg("\"%s\" is not a view", + RelationGetRelationName(targetrelation)))); + ++ /* SELinux checks */ ++ sepgsql_relation_alter_rename(myrelid, newrelname); ++ + /* + * Don't allow ALTER TABLE on composite types. We want people to use ALTER + * TYPE for that. +@@ -2566,6 +2605,27 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, + } + pass = AT_PASS_DROP; + break; ++ case AT_AddSecLabel: ++ ATSimplePermissions(rel, false); ++ /* Performs own recursion */ ++ if (!rel->rd_rel->relhassecids || recursing) ++ ATPrepAddSecLabel(wqueue, rel, recurse, cmd); ++ pass = AT_PASS_ADD_COL; ++ break; ++ case AT_DropSecLabel: ++ ATSimplePermissions(rel, false); ++ /* Performs own recursion */ ++ if (rel->rd_rel->relhassecids) ++ { ++ AlterTableCmd *dropCmd = makeNode(AlterTableCmd); ++ ++ dropCmd->subtype = AT_DropColumn; ++ dropCmd->name = pstrdup("security_label"); ++ dropCmd->behavior = cmd->behavior; ++ ATPrepCmd(wqueue, rel, dropCmd, recurse, false); ++ } ++ pass = AT_PASS_DROP; ++ break; + case AT_SetTableSpace: /* SET TABLESPACE */ + ATSimplePermissionsRelationOrIndex(rel); + /* This command never recurses */ +@@ -2690,7 +2750,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, + case AT_AddColumn: /* ADD COLUMN */ + case AT_AddColumnToView: /* add column via CREATE OR REPLACE + * VIEW */ +- ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false); ++ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false, false); + break; + case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */ + ATExecColumnDefault(rel, cmd->name, cmd->def); +@@ -2762,17 +2822,22 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, + case AT_AddOids: /* SET WITH OIDS */ + /* Use the ADD COLUMN code, unless prep decided to do nothing */ + if (cmd->def != NULL) +- ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true); ++ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true, false); ++ break; ++ case AT_AddSecLabel: /* SET WITH SECURITY LABEL */ ++ /* Use the ADD COLUMN code, unless prep decided to do nothing */ ++ if (cmd->def != NULL) ++ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false, true); + break; + case AT_DropOids: /* SET WITHOUT OIDS */ +- ++ case AT_DropSecLabel: /* SET WITHOUT SECURITY LABEL */ + /* + * Nothing to do here; we'll have generated a DropColumn + * subcommand to do the real work + */ + break; + case AT_SetTableSpace: /* SET TABLESPACE */ +- ++ sepgsql_relation_alter(RelationGetRelid(rel)); + /* + * Nothing to do here; Phase 3 does the work + */ +@@ -3126,6 +3191,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) + MemoryContext oldCxt; + List *dropped_attrs = NIL; + ListCell *lc; ++ Oid tupSecidInherit = InvalidOid; + + econtext = GetPerTupleExprContext(estate); + +@@ -3156,6 +3222,19 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) + } + + /* ++ * If this routine is called due to ALTER TABLE SET WITH SECURITY LABEL, ++ * security label of the relation shall be assigned tuples. ++ */ ++ if (!oldTupDesc->tdhassecid && newTupDesc->tdhassecid) ++ { ++ Oid relSecid = GetSysCacheSecid1(RELOID, ++ ObjectIdGetDatum(tab->relid)); ++ ++ tupSecidInherit = seclabelMoveSecid(RelationGetRelid(oldrel), ++ RelationRelationId, relSecid); ++ } ++ ++ /* + * Scan through the rows, generating a new row if needed and then + * checking all the constraints. + */ +@@ -3172,11 +3251,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) + if (newrel) + { + Oid tupOid = InvalidOid; ++ Oid tupSecid = InvalidOid; + + /* Extract data from old tuple */ + heap_deform_tuple(tuple, oldTupDesc, values, isnull); + if (oldTupDesc->tdhasoid) + tupOid = HeapTupleGetOid(tuple); ++ if (oldTupDesc->tdhassecid) ++ tupSecid = HeapTupleGetSecid(tuple); ++ else ++ tupSecid = tupSecidInherit; + + /* Set dropped attributes to null in new tuple */ + foreach(lc, dropped_attrs) +@@ -3208,6 +3292,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) + /* Preserve OID, if any */ + if (newTupDesc->tdhasoid) + HeapTupleSetOid(tuple, tupOid); ++ /* Preserve security-id, if any */ ++ if (newTupDesc->tdhassecid) ++ HeapTupleSetSecid(tuple, tupSecid); + } + + /* Now check any constraints on the possibly-changed tuple */ +@@ -3608,7 +3695,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, + + static void + ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +- ColumnDef *colDef, bool isOid) ++ ColumnDef *colDef, bool isOid, bool isSecid) + { + Oid myrelid = RelationGetRelid(rel); + Relation pgclass, +@@ -3622,12 +3709,16 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + int32 typmod; + Form_pg_type tform; + Expr *defval; ++ Oid securityId; + + if (rel->rd_rel->reloftype) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot add column to typed table"))); + ++ /* SELinux permission check */ ++ securityId = sepgsql_attribute_create(myrelid, colDef->colname); ++ + attrdesc = heap_open(AttributeRelationId, RowExclusiveLock); + + /* +@@ -3655,6 +3746,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + errmsg("child table \"%s\" has different type for column \"%s\"", + RelationGetRelationName(rel), colDef->colname))); + ++ if (!seclabelCompareSecid(AttributeRelationId, securityId, ++ AttributeRelationId, HeapTupleGetSecid(tuple))) ++ ereport(ERROR, ++ (errcode(ERRCODE_DATATYPE_MISMATCH), ++ errmsg("child table \"%s\" has different label for column \"%s\"", ++ RelationGetRelationName(rel), colDef->colname))); ++ + /* If it's OID, child column must actually be OID */ + if (isOid && childatt->attnum != ObjectIdAttributeNumber) + ereport(ERROR, +@@ -3662,6 +3760,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + errmsg("child table \"%s\" has a conflicting \"%s\" column", + RelationGetRelationName(rel), colDef->colname))); + ++ /* If it's SecID, child column must actually be SecID */ ++ if (isSecid && childatt->attnum != SecurityLabelAttributeNumber) ++ ereport(ERROR, ++ (errcode(ERRCODE_DATATYPE_MISMATCH), ++ errmsg("child table \"%s\" has a conflicting \"%s\" column", ++ RelationGetRelationName(rel), colDef->colname))); ++ + /* Bump the existing child att's inhcount */ + childatt->attinhcount++; + simple_heap_update(attrdesc, &tuple->t_self, tuple); +@@ -3701,6 +3806,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + /* Determine the new attribute's number */ + if (isOid) + newattnum = ObjectIdAttributeNumber; ++ else if (isSecid) ++ newattnum = SecurityLabelAttributeNumber; + else + { + newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1; +@@ -3740,7 +3847,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + + ReleaseSysCache(typeTuple); + +- InsertPgAttributeTuple(attrdesc, &attribute, NULL); ++ InsertPgAttributeTuple(attrdesc, &attribute, NULL, securityId); + + heap_close(attrdesc, RowExclusiveLock); + +@@ -3749,6 +3856,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + */ + if (isOid) + ((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true; ++ else if (isSecid) ++ ((Form_pg_class) GETSTRUCT(reltup))->relhassecids = true; + else + ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum; + +@@ -3860,7 +3969,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + * If we are adding an OID column, we have to tell Phase 3 to rewrite the + * table to fix that. + */ +- if (isOid) ++ if (isOid || isSecid) + tab->new_changeoids = true; + + /* +@@ -3913,6 +4022,31 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) + } + + /* ++ * ALTER TABLE SET WITH SECURITY LABEL ++ * ++ * Basically this is an ADD COLUMN for the special SecLabel column. ++ * We have to cons up a ColumnDef node because the ADD COLUMN code needs one. ++ */ ++static void ++ATPrepAddSecLabel(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) ++{ ++ /* If we're recursing to a child table, the ColumnDef is already set up */ ++ if (cmd->def == NULL) ++ { ++ ColumnDef *cdef = makeNode(ColumnDef); ++ ++ cdef->colname = pstrdup("security_label"); ++ cdef->typeName = makeTypeNameFromOid(TEXTOID, -1); ++ cdef->inhcount = 0; ++ cdef->is_local = true; ++ cdef->is_not_null = true; ++ cdef->storage = 0; ++ cmd->def = (Node *) cdef; ++ } ++ ATPrepAddColumn(wqueue, rel, recurse, cmd); ++} ++ ++/* + * ALTER TABLE ALTER COLUMN DROP NOT NULL + */ + static void +@@ -3924,6 +4058,9 @@ ATExecDropNotNull(Relation rel, const char *colName) + List *indexoidlist; + ListCell *indexoidscan; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + /* + * lookup the attribute + */ +@@ -4014,6 +4151,9 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, + AttrNumber attnum; + Relation attr_rel; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + /* + * lookup the attribute + */ +@@ -4064,6 +4204,9 @@ ATExecColumnDefault(Relation rel, const char *colName, + { + AttrNumber attnum; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + /* + * get the number of the attribute + */ +@@ -4138,6 +4281,9 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue) + HeapTuple tuple; + Form_pg_attribute attrtuple; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + Assert(IsA(newValue, Integer)); + newtarget = intVal(newValue); + +@@ -4204,6 +4350,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, + bool repl_null[Natts_pg_attribute]; + bool repl_repl[Natts_pg_attribute]; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + attrelation = heap_open(AttributeRelationId, RowExclusiveLock); + + tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName); +@@ -4263,6 +4412,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue) + HeapTuple tuple; + Form_pg_attribute attrtuple; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + Assert(IsA(newValue, String)); + storagemode = strVal(newValue); + +@@ -4353,6 +4505,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, + if (recursing) + ATSimplePermissions(rel, false); + ++ /* SELinux checks */ ++ sepgsql_attribute_drop(RelationGetRelid(rel), colName, false); ++ + /* + * get the number of the attribute + */ +@@ -4378,8 +4533,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, + + attnum = targetatt->attnum; + +- /* Can't drop a system attribute, except OID */ +- if (attnum <= 0 && attnum != ObjectIdAttributeNumber) ++ /* Can't drop a system attribute, except OID/SecID */ ++ if (attnum <= 0 && ++ attnum != ObjectIdAttributeNumber && ++ attnum != SecurityLabelAttributeNumber) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot drop system column \"%s\"", +@@ -4495,7 +4652,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, + * If we dropped the OID column, must adjust pg_class.relhasoids and tell + * Phase 3 to physically get rid of the column. + */ +- if (attnum == ObjectIdAttributeNumber) ++ if (attnum == ObjectIdAttributeNumber || ++ attnum == SecurityLabelAttributeNumber) + { + Relation class_rel; + Form_pg_class tuple_class; +@@ -4510,7 +4668,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, + RelationGetRelid(rel)); + tuple_class = (Form_pg_class) GETSTRUCT(tuple); + +- tuple_class->relhasoids = false; ++ if (attnum == ObjectIdAttributeNumber) ++ tuple_class->relhasoids = false; ++ if (attnum == SecurityLabelAttributeNumber) ++ tuple_class->relhassecids = false; ++ + simple_heap_update(class_rel, &tuple->t_self, tuple); + + /* Keep the catalog indexes up to date */ +@@ -4657,6 +4819,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, + if (recursing) + ATSimplePermissions(rel, false); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + /* + * Call AddRelationNewConstraints to do the work, making sure it works on + * a copy of the Constraint so transformExpr can't modify the original. It +@@ -4854,6 +5019,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, + checkFkeyPermissions(pkrel, pkattnum, numpks); + checkFkeyPermissions(rel, fkattnum, numfks); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + /* + * Look up the equality operators to use in the constraint. + * +@@ -5599,6 +5767,9 @@ ATExecDropConstraint(Relation rel, const char *constrName, + if (recursing) + ATSimplePermissions(rel, false); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + + /* +@@ -5931,6 +6102,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, + SysScanDesc scan; + HeapTuple depTup; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + attrelation = heap_open(AttributeRelationId, RowExclusiveLock); + + /* Look up the target column */ +@@ -6544,6 +6718,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_relation_alter(relationOid); + } + + memset(repl_null, false, sizeof(repl_null)); +@@ -6709,6 +6885,9 @@ ATExecClusterOn(Relation rel, const char *indexName) + { + Oid indexOid; + ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace); + + if (!OidIsValid(indexOid)) +@@ -6733,6 +6912,9 @@ ATExecClusterOn(Relation rel, const char *indexName) + static void + ATExecDropCluster(Relation rel) + { ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + mark_index_clustered(rel, InvalidOid); + } + +@@ -6783,6 +6965,9 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) + bool repl_repl[Natts_pg_class]; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; + ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + if (defList == NIL) + return; /* nothing to do */ + +@@ -7115,6 +7300,9 @@ static void + ATExecEnableDisableTrigger(Relation rel, char *trigname, + char fires_when, bool skip_system) + { ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + EnableDisableTrigger(rel, trigname, fires_when, skip_system); + } + +@@ -7127,6 +7315,9 @@ static void + ATExecEnableDisableRule(Relation rel, char *trigname, + char fires_when) + { ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + EnableDisableRule(rel, trigname, fires_when); + } + +@@ -7160,6 +7351,10 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent) + */ + ATSimplePermissions(parent_rel, false); + ++ /* SELinux checks */ ++ sepgsql_relation_alter_inherit(RelationGetRelid(child_rel), ++ RelationGetRelid(parent_rel)); ++ + /* Permanent rels cannot inherit from temporary ones */ + if (parent_rel->rd_istemp && !child_rel->rd_istemp) + ereport(ERROR, +@@ -7512,6 +7707,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent) + List *connames; + bool found = false; + ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + /* + * AccessShareLock on the parent is probably enough, seeing that DROP + * TABLE doesn't lock parent tables at all. We need some lock since we'll +@@ -7809,6 +8007,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, + /* get schema OID and check its permissions */ + nspOid = LookupCreationNamespace(newschema); + ++ /* SELinux checks */ ++ sepgsql_relation_alter_schema(RelationGetRelid(rel), nspOid); ++ + if (oldNspOid == nspOid) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_TABLE), +@@ -8006,6 +8207,282 @@ AlterSeqNamespaces(Relation classRel, Relation rel, + relation_close(depRel, AccessShareLock); + } + ++/* ++ * ALTER TABLE/SEQUENCE/VIEW SECURITY LABEL TO ++ */ ++void ++AlterRelationSecLabelInternal(Oid relOid, Oid securityId, int expected_parents) ++{ ++ Form_pg_class classForm; ++ Relation targetRel; ++ Relation inhRel; ++ Relation classRel; ++ SysScanDesc scan; ++ ScanKeyData skey; ++ HeapTuple tuple; ++ List *indexList; ++ ListCell *cell; ++ int inhcount = 0; ++ ++ /* ++ * Grab an exclusive lock on the target table ++ */ ++ targetRel = relation_open(relOid, AccessExclusiveLock); ++ ++ /* ++ * Check num of inheritors ++ */ ++ inhRel = heap_open(InheritsRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey, ++ Anum_pg_inherits_inhrelid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relOid)); ++ ++ scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, ++ true, SnapshotNow, 1, &skey); ++ while (HeapTupleIsValid(systable_getnext(scan))) ++ inhcount++; ++ ++ systable_endscan(scan); ++ ++ heap_close(inhRel, AccessShareLock); ++ ++ if (inhcount != expected_parents) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_TABLE_DEFINITION), ++ errmsg("cannot relabel inherited relation"))); ++ ++ /* ++ * Update pg_class relation ++ */ ++ classRel = heap_open(RelationRelationId, RowExclusiveLock); ++ ++ tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relOid); ++ ++ classForm = (Form_pg_class) GETSTRUCT(tuple); ++ ++ HeapTupleSetSecid(tuple, securityId); ++ ++ simple_heap_update(classRel, &tuple->t_self, tuple); ++ ++ CatalogUpdateIndexes(classRel, tuple); ++ ++ heap_close(classRel, RowExclusiveLock); ++ ++ /* ++ * Also update TOAST and INDEX ++ */ ++ if (OidIsValid(classForm->reltoastrelid)) ++ AlterRelationSecLabelInternal(classForm->reltoastrelid, ++ securityId, 0); ++ ++ indexList = RelationGetIndexList(targetRel); ++ foreach (cell, indexList) ++ AlterRelationSecLabelInternal(lfirst_oid(cell), securityId, 0); ++ ++ /* ++ * Also update pg_attribute, if not RELKIND_RELATION ++ */ ++ if (classForm->relkind != RELKIND_RELATION) ++ { ++ Relation attRel; ++ HeapTuple oldtup, newtup; ++ Oid attsecid; ++ ++ /* move security-id to pg_attribtue catalog */ ++ attsecid = seclabelMoveSecid(AttributeRelationId, ++ RelationRelationId, ++ securityId); ++ ++ attRel = heap_open(AttributeRelationId, RowExclusiveLock); ++ ++ ScanKeyInit(&skey, ++ Anum_pg_attribute_attrelid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(RelationGetRelid(targetRel))); ++ ++ scan = systable_beginscan(attRel, AttributeRelidNumIndexId, true, ++ SnapshotNow, 1, &skey); ++ while (HeapTupleIsValid(oldtup = systable_getnext(scan))) ++ { ++ Form_pg_attribute attForm ++ = (Form_pg_attribute) GETSTRUCT(oldtup); ++ ++ if (attForm->attinhcount > 0) ++ elog(ERROR, "Bug? attinhcount is %d at %s of %s", ++ attForm->attinhcount, NameStr(attForm->attname), ++ RelationGetRelationName(targetRel)); ++ ++ newtup = heap_copytuple(oldtup); ++ ++ HeapTupleSetSecid(newtup, attsecid); ++ ++ simple_heap_update(attRel, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(attRel, newtup); ++ } ++ systable_endscan(scan); ++ ++ heap_close(attRel, RowExclusiveLock); ++ } ++ ++ heap_close(targetRel, NoLock); /* close rel but keep lock */ ++} ++ ++void ++AlterAttributeSecLabelInternal(Oid relOid, const char *attname, ++ Oid securityId, int expected_parents) ++{ ++ Form_pg_attribute attForm; ++ Relation targetRel; ++ Relation attRel; ++ HeapTuple tuple; ++ ++ /* ++ * Grab an exclusive lock on the target table, which we will NOT ++ * release until end of transaction. ++ */ ++ targetRel = heap_open(relOid, AccessExclusiveLock); ++ ++ attRel = heap_open(AttributeRelationId, RowExclusiveLock); ++ ++ tuple = SearchSysCacheCopyAttName(relOid, attname); ++ if (!HeapTupleIsValid(tuple)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_COLUMN), ++ errmsg("column \"%s\" does not exist", attname))); ++ ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++#if 0 ++ /* ++ * XXX - here is no active reason why we forbid to relabel ++ * system columns. ++ */ ++ if (attForm->attnum <= 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("cannot relabel system column \"%s\"", attname))); ++#endif ++ if (attForm->attinhcount > expected_parents) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_TABLE_DEFINITION), ++ errmsg("cannot relabel inherited column \"%s\"", attname))); ++ ++ /* update pg_attribute */ ++ HeapTupleSetSecid(tuple, securityId); ++ ++ simple_heap_update(attRel, &tuple->t_self, tuple); ++ ++ CatalogUpdateIndexes(attRel, tuple); ++ ++ heap_close(attRel, RowExclusiveLock); ++ ++ heap_close(targetRel, NoLock); /* close rel but keep lock */ ++} ++ ++void ++AlterRelationSecLabel(RangeVar *relation, const char *attname, ++ ObjectType objtype, char *new_label) ++{ ++ Oid relOid = RangeVarGetRelid(relation, false); ++ Oid securityId; ++ char relkind; ++ List *child_oids, *child_numparents; ++ ListCell *lo, *li; ++ ++ /* ++ * Sanity checks for relation types ++ */ ++ relkind = get_rel_relkind(relOid); ++ switch (objtype) ++ { ++ case OBJECT_TABLE: ++ Assert(attname == NULL); ++ if (relkind != RELKIND_RELATION && ++ relkind != RELKIND_SEQUENCE && ++ relkind != RELKIND_VIEW) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a table, sequence or view", ++ get_rel_name(relOid)))); ++ break; ++ ++ case OBJECT_SEQUENCE: ++ Assert(attname == NULL); ++ if (relkind != RELKIND_SEQUENCE) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a sequence", ++ get_rel_name(relOid)))); ++ break; ++ ++ case OBJECT_VIEW: ++ Assert(attname == NULL); ++ if (relkind != RELKIND_VIEW) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a view", ++ get_rel_name(relOid)))); ++ break; ++ ++ case OBJECT_COLUMN: ++ Assert(attname != NULL); ++ if (relkind != RELKIND_RELATION) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a table", ++ get_rel_name(relOid)))); ++ break; ++ ++ default: ++ elog(ERROR, "Bug? unexpected object type %d", objtype); ++ break; ++ } ++ ++ /* ++ * Recursive calls to child relations including myself ++ */ ++ child_oids = find_all_inheritors(relOid, ++ AccessExclusiveLock, ++ &child_numparents); ++ forboth (lo, child_oids, li, child_numparents) ++ { ++ Oid childOid = lfirst_oid(lo); ++ int numParents = lfirst_int(li); ++ Oid relnsp = get_rel_namespace(childOid); ++ ++ /* Permission checks */ ++ if (!pg_class_ownercheck(childOid, GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, ++ get_rel_name(childOid)); ++ ++ if (!allowSystemTableMods && ++ (IsSystemNamespace(relnsp) || IsToastNamespace(relnsp))) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("permission denied: \"%s\" is a system catalog", ++ get_rel_name(childOid)))); ++ ++ /* SELinux checks */ ++ if (objtype != OBJECT_COLUMN) ++ securityId = sepgsql_relation_relabel(childOid, ++ new_label); ++ else ++ securityId = sepgsql_attribute_relabel(childOid, attname, ++ new_label); ++ ++ /* Do work */ ++ if (objtype != OBJECT_COLUMN) ++ AlterRelationSecLabelInternal(childOid, ++ securityId, numParents); ++ else ++ AlterAttributeSecLabelInternal(childOid, attname, ++ securityId, numParents); ++ } ++} + + /* + * This code supports +diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c +index 862cd2d..8ea4118 100644 +--- a/src/backend/commands/tablespace.c ++++ b/src/backend/commands/tablespace.c +@@ -65,6 +65,7 @@ + #include "commands/tablespace.h" + #include "miscadmin.h" + #include "postmaster/bgwriter.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "storage/procarray.h" + #include "storage/standby.h" +@@ -235,6 +236,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) + Oid tablespaceoid; + char *location; + Oid ownerId; ++ Oid securityId; + + /* Must be super user */ + if (!superuser()) +@@ -244,6 +246,9 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) + stmt->tablespacename), + errhint("Must be superuser to create a tablespace."))); + ++ /* SELinux check */ ++ securityId = sepgsql_tablespace_create(stmt->tablespacename); ++ + /* However, the eventual owner of the tablespace need not be */ + if (stmt->owner) + ownerId = get_roleid_checked(stmt->owner); +@@ -324,6 +329,8 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) + + tuple = heap_form_tuple(rel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tuple, securityId); ++ + tablespaceoid = simple_heap_insert(rel, tuple); + + CatalogUpdateIndexes(rel, tuple); +@@ -429,6 +436,9 @@ DropTableSpace(DropTableSpaceStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, + tablespacename); + ++ /* SELinux checks */ ++ sepgsql_tablespace_drop(tablespaceoid, false); ++ + /* Disallow drop of the standard tablespaces, even by superuser */ + if (tablespaceoid == GLOBALTABLESPACE_OID || + tablespaceoid == DEFAULTTABLESPACE_OID) +@@ -787,6 +797,9 @@ RenameTableSpace(const char *oldname, const char *newname) + if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId())) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, oldname); + ++ /* SELinux checks */ ++ sepgsql_tablespace_alter(HeapTupleGetOid(newtuple)); ++ + /* Validate new name */ + if (!allowSystemTableMods && IsReservedName(newname)) + ereport(ERROR, +@@ -868,6 +881,9 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId) + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), newOwnerId); + ++ /* SELinux checks */ ++ sepgsql_tablespace_alter(HeapTupleGetOid(tup)); ++ + /* + * Normally we would also check for create permissions here, but there + * are none for tablespaces so we follow what rename tablespace does +@@ -985,6 +1001,54 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt) + } + + /* ++ * ALTER TABLESPACE xxx SECURITY LABEL TO ... ++ */ ++void ++AlterTableSpaceSecLabel(const char *tspaceName, char *newLabel) ++{ ++ Relation rel; ++ ScanKeyData skey; ++ HeapScanDesc scan; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ Oid securityId; ++ ++ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); ++ ++ /* scan pg_tablespace catalog */ ++ ScanKeyInit(&skey, ++ Anum_pg_tablespace_spcname, ++ BTEqualStrategyNumber, F_NAMEEQ, ++ CStringGetDatum(tspaceName)); ++ scan = heap_beginscan(rel, SnapshotNow, 1, &skey); ++ oldtup = heap_getnext(scan, ForwardScanDirection); ++ if (!HeapTupleIsValid(oldtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("tablespace \"%s\" does not exist", tspaceName))); ++ ++ /* Must be owner */ ++ if (!pg_tablespace_ownercheck(HeapTupleGetOid(oldtup), GetUserId())) ++ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, tspaceName); ++ ++ /* SELinux checks */ ++ securityId = sepgsql_tablespace_relabel(HeapTupleGetSecid(oldtup), newLabel); ++ ++ /* update it */ ++ newtup = heap_copytuple(oldtup); ++ ++ HeapTupleSetSecid(newtup, securityId); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++/* + * Routines for handling the GUC variable 'default_tablespace'. + */ + +diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c +index 2cbc192..2ea9e6e 100644 +--- a/src/backend/commands/trigger.c ++++ b/src/backend/commands/trigger.c +@@ -40,6 +40,7 @@ + #include "parser/parsetree.h" + #include "pgstat.h" + #include "rewrite/rewriteManip.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "tcop/utility.h" + #include "utils/acl.h" +@@ -330,6 +331,10 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, + NameListToString(stmt->funcname)))); + } + ++ /* SELinux checks */ ++ sepgsql_trigger_create(RelationGetRelid(rel), stmt->trigname, ++ constrrelid, funcoid); ++ + /* + * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that + * references one of the built-in RI_FKey trigger functions, assume it is +@@ -1007,6 +1012,9 @@ DropTrigger(Oid relid, const char *trigname, DropBehavior behavior, + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(relid)); + ++ /* SELinux checks */ ++ sepgsql_trigger_drop(relid, trigname, false); ++ + object.classId = TriggerRelationId; + object.objectId = HeapTupleGetOid(tup); + object.objectSubId = 0; +@@ -1116,6 +1124,9 @@ renametrig(Oid relid, + SysScanDesc tgscan; + ScanKeyData key[2]; + ++ /* SELinux checks */ ++ sepgsql_trigger_alter(relid, oldname); ++ + /* + * Grab an exclusive lock on the target table, which we will NOT release + * until end of transaction. +diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c +index ba3de63..19bebb4 100644 +--- a/src/backend/commands/tsearchcmds.c ++++ b/src/backend/commands/tsearchcmds.c +@@ -35,6 +35,7 @@ + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" ++#include "sepgsql/hooks.h" + #include "tsearch/ts_cache.h" + #include "tsearch/ts_public.h" + #include "tsearch/ts_utils.h" +@@ -171,6 +172,7 @@ DefineTSParser(List *names, List *parameters) + NameData pname; + Oid prsOid; + Oid namespaceoid; ++ Oid securityId; + + if (!superuser()) + ereport(ERROR, +@@ -250,6 +252,14 @@ DefineTSParser(List *names, List *parameters) + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("text search parser lextypes method is required"))); + ++ /* SELinux checks */ ++ securityId = sepgsql_ts_parser_create(prsname, namespaceoid, ++ DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prsheadline - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])); ++ + /* + * Looks good, insert + */ +@@ -257,6 +267,8 @@ DefineTSParser(List *names, List *parameters) + + tup = heap_form_tuple(prsRel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + prsOid = simple_heap_insert(prsRel, tup); + + CatalogUpdateIndexes(prsRel, tup); +@@ -314,6 +326,8 @@ RemoveTSParsers(DropStmt *drop) + } + continue; + } ++ /* SELinux checks */ ++ sepgsql_ts_parser_drop(prsOid, false); + + object.classId = TSParserRelationId; + object.objectId = prsOid; +@@ -366,10 +380,13 @@ RenameTSParser(List *oldname, const char *newname) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to rename text search parsers"))); + +- rel = heap_open(TSParserRelationId, RowExclusiveLock); +- + prsId = TSParserGetPrsid(oldname, false); + ++ /* SELinux checks */ ++ sepgsql_ts_parser_alter_rename(prsId, newname); ++ ++ rel = heap_open(TSParserRelationId, RowExclusiveLock); ++ + tup = SearchSysCacheCopy1(TSPARSEROID, ObjectIdGetDatum(prsId)); + + if (!HeapTupleIsValid(tup)) /* should not happen */ +@@ -496,6 +513,7 @@ DefineTSDictionary(List *names, List *parameters) + List *dictoptions = NIL; + Oid dictOid; + Oid namespaceoid; ++ Oid securityId; + AclResult aclresult; + char *dictname; + +@@ -508,6 +526,9 @@ DefineTSDictionary(List *names, List *parameters) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceoid)); + ++ /* SELinux checks */ ++ securityId = sepgsql_ts_dict_create(dictname, namespaceoid); ++ + /* + * loop over the definition list and extract the information we need. + */ +@@ -557,6 +578,8 @@ DefineTSDictionary(List *names, List *parameters) + + tup = heap_form_tuple(dictRel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + dictOid = simple_heap_insert(dictRel, tup); + + CatalogUpdateIndexes(dictRel, tup); +@@ -610,6 +633,8 @@ RenameTSDictionary(List *oldname, const char *newname) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter_rename(dictId, newname); + + namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -674,6 +699,8 @@ RemoveTSDictionaries(DropStmt *drop) + !pg_namespace_ownercheck(namespaceId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, + NameListToString(names)); ++ /* SELinux checks */ ++ sepgsql_ts_dict_drop(dictOid, false); + + object.classId = TSDictionaryRelationId; + object.objectId = dictOid; +@@ -745,6 +772,8 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt) + if (!pg_ts_dict_ownercheck(dictId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, + NameListToString(stmt->dictname)); ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId); + + /* deserialize the existing set of options */ + opt = SysCacheGetAttr(TSDICTOID, tup, +@@ -871,6 +900,8 @@ AlterTSDictionaryOwner(List *name, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId); + + form->dictowner = newOwnerId; + +@@ -982,6 +1013,7 @@ DefineTSTemplate(List *names, List *parameters) + int i; + Oid dictOid; + Oid namespaceoid; ++ Oid securityId; + char *tmplname; + + if (!superuser()) +@@ -1036,6 +1068,10 @@ DefineTSTemplate(List *names, List *parameters) + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("text search template lexize method is required"))); + ++ /* SELinux checks */ ++ securityId = sepgsql_ts_template_create(tmplname, namespaceoid, ++ DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])); + /* + * Looks good, insert + */ +@@ -1044,6 +1080,8 @@ DefineTSTemplate(List *names, List *parameters) + + tup = heap_form_tuple(tmplRel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + dictOid = simple_heap_insert(tmplRel, tup); + + CatalogUpdateIndexes(tmplRel, tup); +@@ -1075,6 +1113,9 @@ RenameTSTemplate(List *oldname, const char *newname) + + tmplId = TSTemplateGetTmplid(oldname, false); + ++ /* SELinux checks */ ++ sepgsql_ts_template_alter_rename(tmplId, newname); ++ + tup = SearchSysCacheCopy1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId)); + + if (!HeapTupleIsValid(tup)) /* should not happen */ +@@ -1145,6 +1186,8 @@ RemoveTSTemplates(DropStmt *drop) + } + continue; + } ++ /* SELinux checks */ ++ sepgsql_ts_template_drop(tmplOid, false); + + object.classId = TSTemplateRelationId; + object.objectId = tmplOid; +@@ -1305,6 +1348,7 @@ DefineTSConfiguration(List *names, List *parameters) + bool nulls[Natts_pg_ts_config]; + AclResult aclresult; + Oid namespaceoid; ++ Oid securityId; + char *cfgname; + NameData cname; + Oid sourceOid = InvalidOid; +@@ -1321,6 +1365,9 @@ DefineTSConfiguration(List *names, List *parameters) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceoid)); + ++ /* SELinux checks */ ++ securityId = sepgsql_ts_config_create(cfgname, namespaceoid); ++ + /* + * loop over the definition list and extract the information we need. + */ +@@ -1388,6 +1435,8 @@ DefineTSConfiguration(List *names, List *parameters) + + tup = heap_form_tuple(cfgRel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + cfgOid = simple_heap_insert(cfgRel, tup); + + CatalogUpdateIndexes(cfgRel, tup); +@@ -1489,6 +1538,9 @@ RenameTSConfiguration(List *oldname, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_ts_config_alter_rename(cfgId, newname); ++ + namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); +@@ -1549,6 +1601,9 @@ RemoveTSConfigurations(DropStmt *drop) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, + NameListToString(names)); + ++ /* SELinux checks */ ++ sepgsql_ts_config_drop(cfgOid, false); ++ + object.classId = TSConfigRelationId; + object.objectId = cfgOid; + object.objectSubId = 0; +@@ -1656,6 +1711,8 @@ AlterTSConfigurationOwner(List *name, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_ts_config_alter(cfgId); + + form->cfgowner = newOwnerId; + +@@ -1693,6 +1750,9 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, + NameListToString(stmt->cfgname)); + ++ /* SELinux checks */ ++ sepgsql_ts_config_alter(HeapTupleGetOid(tup)); ++ + relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock); + + /* Add or drop mappings */ +diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c +index 1e14dca..f7429f0 100644 +--- a/src/backend/commands/typecmds.c ++++ b/src/backend/commands/typecmds.c +@@ -56,6 +56,7 @@ + #include "parser/parse_expr.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -143,6 +144,8 @@ DefineType(List *names, List *parameters) + char *array_type; + Oid array_oid; + Oid typoid; ++ Oid type_replaced; ++ Oid securityId; + Oid resulttype; + ListCell *pl; + +@@ -520,6 +523,15 @@ DefineType(List *names, List *parameters) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(analyzeName)); + #endif ++ /* SELinux checks */ ++ type_replaced = GetSysCacheOid2(TYPENAMENSP, ++ CStringGetDatum(typeName), ++ ObjectIdGetDatum(typeNamespace)); ++ securityId = sepgsql_type_create(typeName, type_replaced, ++ typeNamespace, TYPTYPE_BASE, ++ inputOid, outputOid, ++ receiveOid, sendOid, ++ typmodinOid, typmodoutOid, analyzeOid); + + array_oid = AssignTypeArrayOid(); + +@@ -562,7 +574,8 @@ DefineType(List *names, List *parameters) + storage, /* TOAST strategy */ + -1, /* typMod (Domains only) */ + 0, /* Array Dimensions of typbasetype */ +- false); /* Type NOT NULL */ ++ false, /* Type NOT NULL */ ++ securityId); /* security-id of the type */ + + /* + * Create the array type that goes with it. +@@ -601,7 +614,8 @@ DefineType(List *names, List *parameters) + 'x', /* ARRAY is always toastable */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ +- false); /* Type NOT NULL */ ++ false, /* Type NOT NULL */ ++ securityId); /* security-id of the type */ + + pfree(array_type); + } +@@ -668,6 +682,9 @@ RemoveTypes(DropStmt *drop) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, + format_type_be(typeoid)); + ++ /* SELinux checks */ ++ sepgsql_type_drop(typeoid, false); ++ + if (drop->removeType == OBJECT_DOMAIN) + { + /* Check that this is actually a domain */ +@@ -766,6 +783,7 @@ DefineDomain(CreateDomainStmt *stmt) + Oid basetypeoid; + Oid domainoid; + Oid old_type_oid; ++ Oid securityId; + Form_pg_type baseType; + int32 basetypeMod; + +@@ -1011,6 +1029,13 @@ DefineDomain(CreateDomainStmt *stmt) + } + } + ++ /* SELinux checks */ ++ securityId = sepgsql_type_create(domainName, InvalidOid, ++ domainNamespace, TYPTYPE_DOMAIN, ++ inputProcedure, outputProcedure, ++ receiveProcedure, sendProcedure, ++ InvalidOid, InvalidOid, analyzeProcedure); ++ + /* + * Have TypeCreate do all the real work. + */ +@@ -1044,7 +1069,8 @@ DefineDomain(CreateDomainStmt *stmt) + storage, /* TOAST strategy */ + basetypeMod, /* typeMod value */ + typNDims, /* Array dimensions for base type */ +- typNotNull); /* Type NOT NULL */ ++ typNotNull, /* Type NOT NULL */ ++ securityId); /* security-id of the type */ + + /* + * Process constraints which refer to the domain ID returned by TypeCreate +@@ -1094,6 +1120,7 @@ DefineEnum(CreateEnumStmt *stmt) + AclResult aclresult; + Oid old_type_oid; + Oid enumArrayOid; ++ Oid securityId; + + /* Convert list of names to a name and namespace */ + enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName, +@@ -1120,6 +1147,13 @@ DefineEnum(CreateEnumStmt *stmt) + errmsg("type \"%s\" already exists", enumName))); + } + ++ /* SELinux checks */ ++ securityId = sepgsql_type_create(enumName, old_type_oid, ++ enumNamespace, TYPTYPE_ENUM, ++ F_ENUM_IN, F_ENUM_OUT, ++ F_ENUM_RECV, F_ENUM_SEND, ++ InvalidOid, InvalidOid, InvalidOid); ++ + enumArrayOid = AssignTypeArrayOid(); + + /* Create the pg_type entry */ +@@ -1153,7 +1187,8 @@ DefineEnum(CreateEnumStmt *stmt) + 'p', /* TOAST strategy always plain */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ +- false); /* Type NOT NULL */ ++ false, /* Type NOT NULL */ ++ securityId); /* security-id of the type */ + + /* Enter the enum's values into pg_enum */ + EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid); +@@ -1192,7 +1227,8 @@ DefineEnum(CreateEnumStmt *stmt) + 'x', /* ARRAY is always toastable */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ +- false); /* Type NOT NULL */ ++ false, /* Type NOT NULL */ ++ securityId); /* security-id of the type */ + + pfree(enumArrayName); + } +@@ -1585,6 +1621,8 @@ AlterDomainDefault(List *names, Node *defaultRaw) + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); + ++ sepgsql_type_alter(domainoid); ++ + /* Setup new tuple */ + MemSet(new_record, (Datum) 0, sizeof(new_record)); + MemSet(new_record_nulls, false, sizeof(new_record_nulls)); +@@ -1711,6 +1749,8 @@ AlterDomainNotNull(List *names, bool notNull) + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); + ++ sepgsql_type_alter(domainoid); ++ + /* Is the domain already set to the desired constraint? */ + if (typTup->typnotnull == notNull) + { +@@ -1810,6 +1850,8 @@ AlterDomainDropConstraint(List *names, const char *constrName, + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); + ++ sepgsql_type_alter(domainoid); ++ + /* Grab an appropriate lock on the pg_constraint relation */ + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + +@@ -1884,6 +1926,8 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); + ++ sepgsql_type_alter(domainoid); ++ + if (!IsA(newConstraint, Constraint)) + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(newConstraint)); +@@ -2507,6 +2551,9 @@ RenameType(List *names, const char *newTypeName) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, + format_type_be(typeOid)); + ++ /* SELinux checks */ ++ sepgsql_type_alter_rename(typeOid, newTypeName); ++ + /* + * If it's a composite type, we need to check that it really is a + * free-standing composite type, and not a table's rowtype. We want people +@@ -2627,6 +2674,8 @@ AlterTypeOwner(List *names, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(typTup->typnamespace)); + } ++ /* SELinux checks */ ++ sepgsql_type_alter(HeapTupleGetOid(tup)); + + /* + * If it's a composite type, invoke ATExecChangeOwner so that we fix +@@ -2731,6 +2780,9 @@ AlterTypeNamespace(List *names, const char *newschema) + /* get schema OID and check its permissions */ + nspOid = LookupCreationNamespace(newschema); + ++ /* SELinux checks */ ++ sepgsql_type_alter_schema(typeOid, nspOid); ++ + /* don't allow direct alteration of array types */ + elemOid = get_element_type(typeOid); + if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid) +@@ -2882,3 +2934,100 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, + if (OidIsValid(arrayOid)) + AlterTypeNamespaceInternal(arrayOid, nspOid, true, true); + } ++ ++/* ++ * ALTER TYPE xxx SECURITY LABEL TO ... ++ */ ++void ++AlterTypeSecLabelInternal(Oid typeOid, Oid securityId) ++{ ++ Relation typeRel; ++ Form_pg_type typeForm; ++ HeapTuple tuple; ++ ++ typeRel = heap_open(TypeRelationId, RowExclusiveLock); ++ ++ tuple = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for type %u", typeOid); ++ typeForm = (Form_pg_type) GETSTRUCT(tuple); ++ ++ /* update it */ ++ HeapTupleSetSecid(tuple, securityId); ++ ++ simple_heap_update(typeRel, &tuple->t_self, tuple); ++ ++ CatalogUpdateIndexes(typeRel, tuple); ++ ++ /* if it is a composit type, update pg_class too */ ++ if (OidIsValid(typeForm->typrelid)) ++ { ++ Oid classSecId = seclabelMoveSecid(RelationRelationId, ++ TypeRelationId, ++ securityId); ++ AlterRelationSecLabelInternal(typeForm->typrelid, classSecId, 0); ++ } ++ ++ /* if it has an array type, update that too */ ++ if (OidIsValid(typeForm->typarray)) ++ AlterTypeSecLabelInternal(typeForm->typarray, securityId); ++ ++ heap_freetuple(tuple); ++ ++ heap_close(typeRel, RowExclusiveLock); ++} ++ ++void ++AlterTypeSecLabel(List *names, char *new_label) ++{ ++ Form_pg_type typeForm; ++ HeapTuple typtup; ++ TypeName *typename; ++ Oid typeOid; ++ Oid securityId; ++ ++ /* resolve type name */ ++ typename = makeTypeNameFromNameList(names); ++ typtup = LookupTypeName(NULL, typename, NULL); ++ if (!HeapTupleIsValid(typtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("type \"%s\" does not exist", ++ TypeNameToString(typename)))); ++ ++ typeForm = (Form_pg_type) GETSTRUCT(typtup); ++ typeOid = HeapTupleGetOid(typtup); ++ ++ /* ++ * If it's a composite type, we need to check that it really is a ++ * free-standing composite type, and not a table's rowtype. We want people ++ * to use ALTER TABLE not ALTER TYPE for that case. ++ */ ++ if (typeForm->typtype == TYPTYPE_COMPOSITE && ++ get_rel_relkind(typeForm->typrelid) != RELKIND_COMPOSITE_TYPE) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("%s is a table's row type", ++ format_type_be(typeOid)), ++ errhint("Use ALTER TABLE instead."))); ++ ++ /* don't allow direct alteration of array types, either */ ++ if (OidIsValid(typeForm->typelem) && ++ get_array_type(typeForm->typelem) == typeOid) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("cannot alter array type %s", ++ format_type_be(typeOid)))); ++ ++ /* DAC permission checks */ ++ if (!pg_type_ownercheck(typeOid, GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, ++ format_type_be(typeOid)); ++ ++ /* SELinux checks */ ++ securityId = sepgsql_type_relabel(typeOid, new_label); ++ ++ AlterTypeSecLabelInternal(typeOid, securityId); ++ ++ ReleaseSysCache(typtup); ++} +diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c +index 2f0788e..8de31ae 100644 +--- a/src/backend/commands/user.c ++++ b/src/backend/commands/user.c +@@ -21,11 +21,13 @@ + #include "catalog/pg_authid.h" + #include "catalog/pg_database.h" + #include "catalog/pg_db_role_setting.h" ++#include "catalog/pg_seclabel.h" + #include "commands/comment.h" + #include "commands/dbcommands.h" + #include "commands/user.h" + #include "libpq/md5.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "storage/lmgr.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -111,6 +113,7 @@ CreateRole(CreateRoleStmt *stmt) + DefElem *drolemembers = NULL; + DefElem *dadminmembers = NULL; + DefElem *dvalidUntil = NULL; ++ Oid securityId; + + /* The defaults can vary depending on the original statement type */ + switch (stmt->stmt_type) +@@ -279,6 +282,9 @@ CreateRole(CreateRoleStmt *stmt) + errmsg("permission denied to create role"))); + } + ++ /* SELinux checks */ ++ securityId = sepgsql_role_create(stmt->role); ++ + if (strcmp(stmt->role, "public") == 0 || + strcmp(stmt->role, "none") == 0) + ereport(ERROR, +@@ -365,6 +371,8 @@ CreateRole(CreateRoleStmt *stmt) + + tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls); + ++ HeapTupleSetSecid(tuple, securityId); ++ + /* + * Insert new record in the pg_authid table + */ +@@ -606,6 +614,8 @@ AlterRole(AlterRoleStmt *stmt) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied"))); + } ++ /* SELinux checks */ ++ sepgsql_role_alter(roleid); + + /* Convert validuntil to internal form */ + if (validUntil) +@@ -791,6 +801,8 @@ AlterRoleSet(AlterRoleSetStmt *stmt) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied"))); + } ++ /* SELinux checks */ ++ sepgsql_role_alter(HeapTupleGetOid(roletuple)); + + /* look up and lock the database, if specified */ + if (stmt->database != NULL) +@@ -886,6 +898,9 @@ DropRole(DropRoleStmt *stmt) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to drop superusers"))); + ++ /* SELinux checks */ ++ sepgsql_role_drop(roleid, false); ++ + /* + * Lock the role, so nobody can add dependencies to her while we drop + * her. We keep the lock until the end of transaction. +@@ -1282,6 +1297,9 @@ AddRoleMems(const char *rolename, Oid roleid, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to set grantor"))); + ++ /* SELinux checks */ ++ sepgsql_role_grant(roleid, true, memberIds); ++ + pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock); + pg_authmem_dsc = RelationGetDescr(pg_authmem_rel); + +@@ -1412,6 +1430,8 @@ DelRoleMems(const char *rolename, Oid roleid, + errmsg("must have admin option on role \"%s\"", + rolename))); + } ++ /* SELinux checks */ ++ sepgsql_role_grant(roleid, false, memberIds); + + pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock); + pg_authmem_dsc = RelationGetDescr(pg_authmem_rel); +diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c +index 49a206e..4646c41 100644 +--- a/src/backend/commands/vacuum.c ++++ b/src/backend/commands/vacuum.c +@@ -980,6 +980,12 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound, + relation_close(onerel, NoLock); + + /* ++ * VACUUM FULL also reclaim orphan security labels, if exist ++ */ ++ if (vacstmt->options & VACOPT_FULL) ++ seclabelRelationReclaim(relid); ++ ++ /* + * Complete the transaction and free all temporary memory used. + */ + PopActiveSnapshot(); +diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c +index d7a06bc..5480c83 100644 +--- a/src/backend/commands/view.c ++++ b/src/backend/commands/view.c +@@ -28,6 +28,7 @@ + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteManip.h" + #include "rewrite/rewriteSupport.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -167,6 +168,9 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + ++ /* SELinux checks */ ++ sepgsql_view_replace(viewOid); ++ + /* Also check it's not in use already */ + CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW"); + +diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c +index 5e555ad..963096d 100644 +--- a/src/backend/executor/execJunk.c ++++ b/src/backend/executor/execJunk.c +@@ -58,7 +58,8 @@ + * An optional resultSlot can be passed as well. + */ + JunkFilter * +-ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) ++ExecInitJunkFilter(List *targetList, bool hasoid, bool hassecid, ++ TupleTableSlot *slot) + { + JunkFilter *junkfilter; + TupleDesc cleanTupType; +@@ -70,7 +71,7 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) + /* + * Compute the tuple descriptor for the cleaned tuple. + */ +- cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); ++ cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hassecid); + + /* + * Use the given slot, or make a new slot if we weren't given one. +diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c +index d5e7e3a..96d4ba8 100644 +--- a/src/backend/executor/execMain.c ++++ b/src/backend/executor/execMain.c +@@ -38,6 +38,7 @@ + #include "access/xact.h" + #include "catalog/heap.h" + #include "catalog/namespace.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/toasting.h" + #include "commands/tablespace.h" + #include "commands/trigger.h" +@@ -47,11 +48,13 @@ + #include "optimizer/clauses.h" + #include "parser/parse_clause.h" + #include "parser/parsetree.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/smgr.h" + #include "tcop/utility.h" + #include "utils/acl.h" ++#include "utils/guc.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" + #include "utils/snapmgr.h" +@@ -414,7 +417,16 @@ ExecCheckRTPerms(List *rangeTable) + + foreach(l, rangeTable) + { +- ExecCheckRTEPerms((RangeTblEntry *) lfirst(l)); ++ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); ++ ++ ExecCheckRTEPerms(rte); ++ ++ if (rte->rtekind == RTE_RELATION) ++ sepgsql_relation_perms(rte->relid, ++ rte->requiredPerms, ++ rte->selectedCols, ++ rte->modifiedCols, ++ true); + } + } + +@@ -829,6 +841,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) + + j = ExecInitJunkFilter(planstate->plan->targetlist, + tupType->tdhasoid, ++ tupType->tdhassecid, + ExecInitExtraTupleSlot(estate)); + estate->es_junkFilter = j; + +@@ -1065,6 +1078,37 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids) + return false; + } + ++/* ++ * ExecContextForcesSecids ++ * ++ * It is same with ExecContextForcesOids, except for it checks space ++ * for security id of the tuples. ++ */ ++bool ++ExecContextForcesSecids(PlanState *planstate, bool *hassecid) ++{ ++ ResultRelInfo *ri = planstate->state->es_result_relation_info; ++ ++ if (ri != NULL) ++ { ++ Relation rel = ri->ri_RelationDesc; ++ ++ if (rel != NULL) ++ { ++ *hassecid = RelationGetForm(rel)->relhassecids; ++ return true; ++ } ++ } ++ ++ if (planstate->state->es_select_into) ++ { ++ *hassecid = default_with_secids; ++ return true; ++ } ++ ++ return false; ++} ++ + /* ---------------------------------------------------------------- + * ExecEndPlan + * +@@ -2073,6 +2117,7 @@ OpenIntoRel(QueryDesc *queryDesc) + Oid intoRelationId; + TupleDesc tupdesc; + DR_intorel *myState; ++ Oid *secLabels; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; + + Assert(into); +@@ -2144,6 +2189,14 @@ OpenIntoRel(QueryDesc *queryDesc) + get_tablespace_name(tablespaceId)); + } + ++ /* SELinux checks */ ++ secLabels = sepgsql_relation_create(intoName, ++ RELKIND_RELATION, ++ queryDesc->tupDesc, ++ namespaceId, ++ NIL, ++ true); ++ + /* Parse and validate any reloptions */ + reloptions = transformRelOptions((Datum) 0, + into->options, +@@ -2174,7 +2227,8 @@ OpenIntoRel(QueryDesc *queryDesc) + into->onCommit, + reloptions, + true, +- allowSystemTableMods); ++ allowSystemTableMods, ++ secLabels); + + FreeTupleDesc(tupdesc); + +@@ -2305,6 +2359,11 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self) + */ + if (myState->rel->rd_rel->relhasoids) + HeapTupleSetOid(tuple, InvalidOid); ++ if (myState->rel->rd_rel->relhassecids) ++ HeapTupleSetSecid(tuple, InvalidOid); ++ ++ /* SELinux checks */ ++ sepgsql_tuple_insert(myState->rel, tuple); + + heap_insert(myState->rel, + tuple, +diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c +index 005e15e..6e93932 100644 +--- a/src/backend/executor/execQual.c ++++ b/src/backend/executor/execQual.c +@@ -49,6 +49,7 @@ + #include "optimizer/planner.h" + #include "parser/parse_coerce.h" + #include "pgstat.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -1177,6 +1178,9 @@ init_fcache(Oid foid, FuncExprState *fcache, + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); + ++ /* SELinux checks */ ++ sepgsql_proc_execute(foid); ++ + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if +@@ -1221,7 +1225,7 @@ init_fcache(Oid foid, FuncExprState *fcache, + else if (functypclass == TYPEFUNC_SCALAR) + { + /* Base data type, i.e. scalar */ +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, + (AttrNumber) 1, + NULL, +@@ -2111,7 +2115,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr, + /* + * Scalar type, so make a single-column descriptor + */ +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, + (AttrNumber) 1, + "column", +@@ -4108,6 +4112,9 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(acoerce->elemfuncid)); + ++ /* SELinux checks */ ++ sepgsql_proc_execute(acoerce->elemfuncid); ++ + /* Set up the primary fmgr lookup information */ + fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), + econtext->ecxt_per_query_memory); +diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c +index 53fe195..cb63671 100644 +--- a/src/backend/executor/execScan.c ++++ b/src/backend/executor/execScan.c +@@ -258,6 +258,7 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc + int numattrs = tupdesc->natts; + int attrno; + bool hasoid; ++ bool hassecid; + ListCell *tlist_item = list_head(tlist); + + /* Check the tlist attributes */ +@@ -307,6 +308,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc + if (ExecContextForcesOids(ps, &hasoid) && + hasoid != tupdesc->tdhasoid) + return false; ++ if (ExecContextForcesSecids(ps, &hassecid) && ++ hassecid != tupdesc->tdhassecid) ++ return false; + + return true; + } +diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c +index 1caf976..64f6468 100644 +--- a/src/backend/executor/execTuples.c ++++ b/src/backend/executor/execTuples.c +@@ -98,7 +98,7 @@ + + + static TupleDesc ExecTypeFromTLInternal(List *targetList, +- bool hasoid, bool skipjunk); ++ bool hasoid, bool hassecid, bool skipjunk); + + + /* ---------------------------------------------------------------- +@@ -899,9 +899,9 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType) + * ---------------------------------------------------------------- + */ + TupleDesc +-ExecTypeFromTL(List *targetList, bool hasoid) ++ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid) + { +- return ExecTypeFromTLInternal(targetList, hasoid, false); ++ return ExecTypeFromTLInternal(targetList, hasoid, hassecid, false); + } + + /* ---------------------------------------------------------------- +@@ -911,13 +911,14 @@ ExecTypeFromTL(List *targetList, bool hasoid) + * ---------------------------------------------------------------- + */ + TupleDesc +-ExecCleanTypeFromTL(List *targetList, bool hasoid) ++ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid) + { +- return ExecTypeFromTLInternal(targetList, hasoid, true); ++ return ExecTypeFromTLInternal(targetList, hasoid, hassecid, true); + } + + static TupleDesc +-ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) ++ExecTypeFromTLInternal(List *targetList, ++ bool hasoid, bool hassecid, bool skipjunk) + { + TupleDesc typeInfo; + ListCell *l; +@@ -928,7 +929,7 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) + len = ExecCleanTargetListLength(targetList); + else + len = ExecTargetListLength(targetList); +- typeInfo = CreateTemplateTupleDesc(len, hasoid); ++ typeInfo = CreateTemplateTupleDesc(len, hasoid, hassecid); + + foreach(l, targetList) + { +@@ -960,7 +961,7 @@ ExecTypeFromExprList(List *exprList) + int cur_resno = 1; + char fldname[NAMEDATALEN]; + +- typeInfo = CreateTemplateTupleDesc(list_length(exprList), false); ++ typeInfo = CreateTemplateTupleDesc(list_length(exprList), false, false); + + foreach(l, exprList) + { +diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c +index 98e4a64..14f9404 100644 +--- a/src/backend/executor/execUtils.c ++++ b/src/backend/executor/execUtils.c +@@ -444,6 +444,7 @@ void + ExecAssignResultTypeFromTL(PlanState *planstate) + { + bool hasoid; ++ bool hassecid; + TupleDesc tupDesc; + + if (ExecContextForcesOids(planstate, &hasoid)) +@@ -456,12 +457,15 @@ ExecAssignResultTypeFromTL(PlanState *planstate) + hasoid = false; + } + ++ if (!ExecContextForcesSecids(planstate, &hassecid)) ++ hassecid = false; ++ + /* + * ExecTypeFromTL needs the parse-time representation of the tlist, not a + * list of ExprStates. This is good because some plan nodes don't bother + * to set up planstate->targetlist ... + */ +- tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid); ++ tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid, hassecid); + ExecAssignResultType(planstate, tupDesc); + } + +diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c +index d552698..ed1913f 100644 +--- a/src/backend/executor/functions.c ++++ b/src/backend/executor/functions.c +@@ -1153,7 +1153,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, + + /* Set up junk filter if needed */ + if (junkFilter) +- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); ++ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + } + else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) + { +@@ -1192,7 +1192,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, + } + /* Set up junk filter if needed */ + if (junkFilter) +- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); ++ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + return false; /* NOT returning whole tuple */ + } + } +@@ -1205,7 +1205,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, + * what the caller expects will happen at runtime. + */ + if (junkFilter) +- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); ++ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + return true; + } + Assert(tupdesc); +diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c +index ddd91fc..cfb685e 100644 +--- a/src/backend/executor/nodeAgg.c ++++ b/src/backend/executor/nodeAgg.c +@@ -89,6 +89,7 @@ + #include "optimizer/tlist.h" + #include "parser/parse_agg.h" + #include "parser/parse_coerce.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -1641,6 +1642,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) + get_func_name(finalfn_oid)); + } + } ++ /* SELinux checks */ ++ sepgsql_aggregate_execute(aggref->aggfnoid); + + /* resolve actual type of transition state, if polymorphic */ + aggtranstype = aggform->aggtranstype; +@@ -1722,7 +1725,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) + * Get a tupledesc corresponding to the inputs (including sort + * expressions) of the agg. + */ +- peraggstate->evaldesc = ExecTypeFromTL(aggref->args, false); ++ peraggstate->evaldesc = ExecTypeFromTL(aggref->args, false, false); + + /* Create slot we're going to do argument evaluation in */ + peraggstate->evalslot = ExecInitExtraTupleSlot(estate); +diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c +index 66e6b74..a31907c 100644 +--- a/src/backend/executor/nodeFunctionscan.c ++++ b/src/backend/executor/nodeFunctionscan.c +@@ -178,7 +178,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) + /* Base data type, i.e. scalar */ + char *attname = strVal(linitial(node->funccolnames)); + +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, + (AttrNumber) 1, + attname, +diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c +index ca6b0f8..b5c79d0 100644 +--- a/src/backend/executor/nodeMergejoin.c ++++ b/src/backend/executor/nodeMergejoin.c +@@ -98,6 +98,7 @@ + #include "executor/execdefs.h" + #include "executor/nodeMergejoin.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" +@@ -216,6 +217,9 @@ MJExamineQuals(List *mergeclauses, + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(cmpproc)); + ++ /* SELinux permissions */ ++ sepgsql_proc_execute(cmpproc); ++ + /* Set up the fmgr lookup information */ + fmgr_info(cmpproc, &(clause->cmpfinfo)); + +diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c +index 7856b66..eee2739 100644 +--- a/src/backend/executor/nodeModifyTable.c ++++ b/src/backend/executor/nodeModifyTable.c +@@ -38,11 +38,13 @@ + #include "postgres.h" + + #include "access/xact.h" ++#include "catalog/pg_seclabel.h" + #include "commands/trigger.h" + #include "executor/executor.h" + #include "executor/nodeModifyTable.h" + #include "miscadmin.h" + #include "nodes/nodeFuncs.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "utils/builtins.h" + #include "utils/memutils.h" +@@ -160,7 +162,8 @@ ExecProcessReturning(ProjectionInfo *projectReturning, + static TupleTableSlot * + ExecInsert(TupleTableSlot *slot, + TupleTableSlot *planSlot, +- EState *estate) ++ EState *estate, ++ Oid securityId) + { + HeapTuple tuple; + ResultRelInfo *resultRelInfo; +@@ -194,6 +197,8 @@ ExecInsert(TupleTableSlot *slot, + */ + if (resultRelationDesc->rd_rel->relhasoids) + HeapTupleSetOid(tuple, InvalidOid); ++ if (resultRelationDesc->rd_rel->relhassecids) ++ HeapTupleSetSecid(tuple, securityId); + + /* BEFORE ROW INSERT Triggers */ + if (resultRelInfo->ri_TrigDesc && +@@ -224,6 +229,8 @@ ExecInsert(TupleTableSlot *slot, + tuple = newtuple; + } + } ++ /* SELinux checks */ ++ sepgsql_tuple_insert(resultRelationDesc, tuple); + + /* + * Check the constraints of the tuple +@@ -421,7 +428,8 @@ ExecUpdate(ItemPointer tupleid, + TupleTableSlot *slot, + TupleTableSlot *planSlot, + EPQState *epqstate, +- EState *estate) ++ EState *estate, ++ Oid securityId) + { + HeapTuple tuple; + ResultRelInfo *resultRelInfo; +@@ -449,6 +457,13 @@ ExecUpdate(ItemPointer tupleid, + resultRelInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelInfo->ri_RelationDesc; + ++ /* ++ * If the result relation has writable system attributes, ++ * we store user given value (or InvalidOid) on the tuple. ++ */ ++ if (resultRelationDesc->rd_rel->relhassecids) ++ HeapTupleSetSecid(tuple, securityId); ++ + /* BEFORE ROW UPDATE Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) +@@ -479,6 +494,8 @@ ExecUpdate(ItemPointer tupleid, + tuple = newtuple; + } + } ++ /* SELinux checks */ ++ sepgsql_tuple_update(resultRelationDesc, tupleid, tuple); + + /* + * Check the constraints of the tuple +@@ -635,6 +652,42 @@ fireASTriggers(ModifyTableState *node) + } + } + ++/* ++ * FetchWritableSecLabel ++ * ++ * It moves user given security label into slot-> ++ */ ++static Oid ++FetchWritableSecLabel(Relation relation, ++ JunkFilter *junkfilter, ++ TupleTableSlot *slot) ++{ ++ Oid securityId = InvalidOid; ++ AttrNumber attno; ++ Datum datum; ++ bool isnull; ++ char *label; ++ ++ /* ++ * If no explicit label was given, set a default label later ++ */ ++ attno = ExecFindJunkAttribute(junkfilter, "security_label"); ++ if (attno != InvalidAttrNumber && !ignore_security_label_input) ++ { ++ datum = ExecGetJunkAttribute(slot, attno, &isnull); ++ if (isnull) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set NULL on \"security_label\""))); ++ ++ label = TextDatumGetCString(datum); ++ ++ securityId = seclabelTransInput(RelationGetRelid(relation), label); ++ ++ pfree(label); ++ } ++ return securityId; ++} + + /* ---------------------------------------------------------------- + * ExecModifyTable +@@ -682,6 +735,8 @@ ExecModifyTable(ModifyTableState *node) + */ + for (;;) + { ++ Oid securityId = InvalidOid; ++ + planSlot = ExecProcNode(subplanstate); + + if (TupIsNull(planSlot)) +@@ -705,6 +760,14 @@ ExecModifyTable(ModifyTableState *node) + + if (junkfilter != NULL) + { ++ Relation targetRel ++ = estate->es_result_relation_info->ri_RelationDesc; ++ ++ /* ++ * extract writable system column ++ */ ++ securityId = FetchWritableSecLabel(targetRel, junkfilter, slot); ++ + /* + * extract the 'ctid' junk attribute. + */ +@@ -734,11 +797,11 @@ ExecModifyTable(ModifyTableState *node) + switch (operation) + { + case CMD_INSERT: +- slot = ExecInsert(slot, planSlot, estate); ++ slot = ExecInsert(slot, planSlot, estate, securityId); + break; + case CMD_UPDATE: + slot = ExecUpdate(tupleid, slot, planSlot, +- &node->mt_epqstate, estate); ++ &node->mt_epqstate, estate, securityId); + break; + case CMD_DELETE: + slot = ExecDelete(tupleid, planSlot, +@@ -850,7 +913,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) + * RETURNING list. We assume the rest will look the same. + */ + tupDesc = ExecTypeFromTL((List *) linitial(node->returningLists), +- false); ++ false, false); + + /* Set up a slot for the output of the RETURNING projection(s) */ + ExecInitResultTupleSlot(estate, &mtstate->ps); +@@ -884,7 +947,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) + * We still must construct a dummy result tuple type, because InitPlan + * expects one (maybe should change that?). + */ +- tupDesc = ExecTypeFromTL(NIL, false); ++ tupDesc = ExecTypeFromTL(NIL, false, false); + ExecInitResultTupleSlot(estate, &mtstate->ps); + ExecAssignResultType(&mtstate->ps, tupDesc); + +@@ -976,6 +1039,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) + + j = ExecInitJunkFilter(subplan->targetlist, + resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, ++ resultRelInfo->ri_RelationDesc->rd_att->tdhassecid, + ExecInitExtraTupleSlot(estate)); + + if (operation == CMD_UPDATE || operation == CMD_DELETE) +diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c +index 9f1ff16..bef704b 100644 +--- a/src/backend/executor/nodeSubplan.c ++++ b/src/backend/executor/nodeSubplan.c +@@ -859,7 +859,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) + * (hack alert!). The righthand expressions will be evaluated in our + * own innerecontext. + */ +- tupDesc = ExecTypeFromTL(leftptlist, false); ++ tupDesc = ExecTypeFromTL(leftptlist, false, false); + slot = ExecInitExtraTupleSlot(estate); + ExecSetSlotDescriptor(slot, tupDesc); + sstate->projLeft = ExecBuildProjectionInfo(lefttlist, +@@ -867,7 +867,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) + slot, + NULL); + +- tupDesc = ExecTypeFromTL(rightptlist, false); ++ tupDesc = ExecTypeFromTL(rightptlist, false, false); + slot = ExecInitExtraTupleSlot(estate); + ExecSetSlotDescriptor(slot, tupDesc); + sstate->projRight = ExecBuildProjectionInfo(righttlist, +diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c +index 712bab1..e69f3c7 100644 +--- a/src/backend/executor/nodeWindowAgg.c ++++ b/src/backend/executor/nodeWindowAgg.c +@@ -43,6 +43,7 @@ + #include "optimizer/clauses.h" + #include "parser/parse_agg.h" + #include "parser/parse_coerce.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/datum.h" +@@ -1554,6 +1555,12 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(wfunc->winfnoid)); + ++ /* SELinux checks */ ++ if (wfunc->winagg) ++ sepgsql_aggregate_execute(wfunc->winfnoid); ++ else ++ sepgsql_proc_execute(wfunc->winfnoid); ++ + /* Fill in the perfuncstate data */ + perfuncstate->wfuncstate = wfuncstate; + perfuncstate->wfunc = wfunc; +diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c +index 7f0b5e4..2e2c63b 100644 +--- a/src/backend/executor/spi.c ++++ b/src/backend/executor/spi.c +@@ -767,6 +767,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, + mtuple->t_tableOid = tuple->t_tableOid; + if (rel->rd_att->tdhasoid) + HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); ++ if (rel->rd_att->tdhassecid) ++ HeapTupleSetSecid(mtuple, HeapTupleGetSecid(tuple)); + } + else + { +@@ -795,7 +797,8 @@ SPI_fnumber(TupleDesc tupdesc, const char *fname) + return res + 1; + } + +- sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ ); ++ /* "oid" and "security_label" will be accepted */ ++ sysatt = SystemAttributeByName(fname, true, true); + if (sysatt != NULL) + return sysatt->attnum; + +@@ -820,7 +823,7 @@ SPI_fname(TupleDesc tupdesc, int fnumber) + if (fnumber > 0) + att = tupdesc->attrs[fnumber - 1]; + else +- att = SystemAttributeDefinition(fnumber, true); ++ att = SystemAttributeDefinition(fnumber, true, true); + + return pstrdup(NameStr(att->attname)); + } +@@ -852,7 +855,7 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) + if (fnumber > 0) + typoid = tupdesc->attrs[fnumber - 1]->atttypid; + else +- typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; ++ typoid = (SystemAttributeDefinition(fnumber, true, true))->atttypid; + + getTypeOutputInfo(typoid, &foutoid, &typisvarlena); + +@@ -909,7 +912,7 @@ SPI_gettype(TupleDesc tupdesc, int fnumber) + if (fnumber > 0) + typoid = tupdesc->attrs[fnumber - 1]->atttypid; + else +- typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; ++ typoid = (SystemAttributeDefinition(fnumber, true, true))->atttypid; + + typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid)); + +@@ -939,7 +942,7 @@ SPI_gettypeid(TupleDesc tupdesc, int fnumber) + if (fnumber > 0) + return tupdesc->attrs[fnumber - 1]->atttypid; + else +- return (SystemAttributeDefinition(fnumber, true))->atttypid; ++ return (SystemAttributeDefinition(fnumber, true, true))->atttypid; + } + + char * +diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c +index f4f50f8..4ab5698 100644 +--- a/src/backend/libpq/be-fsstubs.c ++++ b/src/backend/libpq/be-fsstubs.c +@@ -46,6 +46,7 @@ + #include "libpq/be-fsstubs.h" + #include "libpq/libpq-fs.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "storage/large_object.h" + #include "utils/acl.h" +@@ -172,6 +173,8 @@ lo_read(int fd, char *buf, int len) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for large object %u", + cookies[fd]->id))); ++ /* SELinux checks */ ++ sepgsql_largeobject_read(cookies[fd]->id, cookies[fd]->snapshot); + + status = inv_read(cookies[fd], buf, len); + +@@ -204,6 +207,8 @@ lo_write(int fd, const char *buf, int len) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for large object %u", + cookies[fd]->id))); ++ /* SELinux checks */ ++ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot); + + status = inv_write(cookies[fd], buf, len); + +@@ -233,6 +238,7 @@ Datum + lo_creat(PG_FUNCTION_ARGS) + { + Oid lobjId; ++ Oid securityId; + + /* + * We don't actually need to store into fscxt, but create it anyway to +@@ -240,7 +246,10 @@ lo_creat(PG_FUNCTION_ARGS) + */ + CreateFSContext(); + +- lobjId = inv_create(InvalidOid); ++ /* SELinux checks */ ++ securityId = sepgsql_largeobject_create(InvalidOid); ++ ++ lobjId = inv_create(InvalidOid, securityId); + + PG_RETURN_OID(lobjId); + } +@@ -249,6 +258,7 @@ Datum + lo_create(PG_FUNCTION_ARGS) + { + Oid lobjId = PG_GETARG_OID(0); ++ Oid securityId; + + /* + * We don't actually need to store into fscxt, but create it anyway to +@@ -256,7 +266,10 @@ lo_create(PG_FUNCTION_ARGS) + */ + CreateFSContext(); + +- lobjId = inv_create(lobjId); ++ /* SELinux checks */ ++ securityId = sepgsql_largeobject_create(lobjId); ++ ++ lobjId = inv_create(lobjId, securityId); + + PG_RETURN_OID(lobjId); + } +@@ -286,6 +299,9 @@ lo_unlink(PG_FUNCTION_ARGS) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be owner of large object %u", lobjId))); + ++ /* SELinux checks */ ++ sepgsql_largeobject_drop(lobjId, false); ++ + /* + * If there are any open LO FDs referencing that ID, close 'em. + */ +@@ -381,9 +397,10 @@ lo_import_internal(text *filename, Oid lobjOid) + int nbytes, + tmp; + char buf[BUFSIZE]; +- char fnamebuf[MAXPGPATH]; ++ char *fnamebuf = text_to_cstring(filename); + LargeObjectDesc *lobj; + Oid oid; ++ Oid securityId; + + #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS + if (!superuser()) +@@ -392,13 +409,14 @@ lo_import_internal(text *filename, Oid lobjOid) + errmsg("must be superuser to use server-side lo_import()"), + errhint("Anyone can use the client-side lo_import() provided by libpq."))); + #endif +- + CreateFSContext(); + ++ /* SELinux checks */ ++ securityId = sepgsql_largeobject_import(lobjOid, fnamebuf); ++ + /* + * open the file to be read in + */ +- text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); + fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666); + if (fd < 0) + ereport(ERROR, +@@ -409,7 +427,7 @@ lo_import_internal(text *filename, Oid lobjOid) + /* + * create an inversion object + */ +- oid = inv_create(lobjOid); ++ oid = inv_create(lobjOid, securityId); + + /* + * read in from the filesystem and write to the inversion object +@@ -447,7 +465,7 @@ lo_export(PG_FUNCTION_ARGS) + int nbytes, + tmp; + char buf[BUFSIZE]; +- char fnamebuf[MAXPGPATH]; ++ char *fnamebuf = text_to_cstring(filename); + LargeObjectDesc *lobj; + mode_t oumask; + +@@ -458,7 +476,6 @@ lo_export(PG_FUNCTION_ARGS) + errmsg("must be superuser to use server-side lo_export()"), + errhint("Anyone can use the client-side lo_export() provided by libpq."))); + #endif +- + CreateFSContext(); + + /* +@@ -466,6 +483,9 @@ lo_export(PG_FUNCTION_ARGS) + */ + lobj = inv_open(lobjId, INV_READ, fscxt); + ++ /* SELinux checks */ ++ sepgsql_largeobject_export(lobj->id, lobj->snapshot, fnamebuf); ++ + /* + * open the file to be written to + * +@@ -473,7 +493,6 @@ lo_export(PG_FUNCTION_ARGS) + * 022. This code used to drop it all the way to 0, but creating + * world-writable export files doesn't seem wise. + */ +- text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); + oumask = umask((mode_t) 0022); + fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666); + umask(oumask); +@@ -528,6 +547,9 @@ lo_truncate(PG_FUNCTION_ARGS) + errmsg("permission denied for large object %u", + cookies[fd]->id))); + ++ /* SELinux checks */ ++ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot); ++ + inv_truncate(cookies[fd], len); + + PG_RETURN_INT32(0); +diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c +index e770e89..9642a66 100644 +--- a/src/backend/nodes/copyfuncs.c ++++ b/src/backend/nodes/copyfuncs.c +@@ -1823,6 +1823,7 @@ _copyRangeTblEntry(RangeTblEntry *from) + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(modifiedCols); ++ COPY_SCALAR_FIELD(rowlvPerms); + + return newnode; + } +@@ -2755,6 +2756,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) + return newnode; + } + ++static AlterSecLabelStmt * ++_copyAlterSecLabelStmt(AlterSecLabelStmt *from) ++{ ++ AlterSecLabelStmt *newnode = makeNode(AlterSecLabelStmt); ++ ++ COPY_SCALAR_FIELD(objectType); ++ COPY_NODE_FIELD(relation); ++ COPY_NODE_FIELD(object); ++ COPY_NODE_FIELD(objarg); ++ COPY_STRING_FIELD(addname); ++ COPY_NODE_FIELD(secLabel); ++ ++ return newnode; ++} ++ + static RuleStmt * + _copyRuleStmt(RuleStmt *from) + { +@@ -3968,6 +3984,9 @@ copyObject(void *from) + case T_AlterOwnerStmt: + retval = _copyAlterOwnerStmt(from); + break; ++ case T_AlterSecLabelStmt: ++ retval = _copyAlterSecLabelStmt(from); ++ break; + case T_RuleStmt: + retval = _copyRuleStmt(from); + break; +diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c +index 5d83727..fd4071d 100644 +--- a/src/backend/nodes/equalfuncs.c ++++ b/src/backend/nodes/equalfuncs.c +@@ -1308,6 +1308,19 @@ _equalAlterOwnerStmt(AlterOwnerStmt *a, AlterOwnerStmt *b) + } + + static bool ++_equalAlterSecLabelStmt(AlterSecLabelStmt *a, AlterSecLabelStmt *b) ++{ ++ COMPARE_SCALAR_FIELD(objectType); ++ COMPARE_NODE_FIELD(relation); ++ COMPARE_NODE_FIELD(object); ++ COMPARE_NODE_FIELD(objarg); ++ COMPARE_STRING_FIELD(addname); ++ COMPARE_NODE_FIELD(secLabel); ++ ++ return true; ++} ++ ++static bool + _equalRuleStmt(RuleStmt *a, RuleStmt *b) + { + COMPARE_NODE_FIELD(relation); +@@ -2186,6 +2199,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) + COMPARE_SCALAR_FIELD(checkAsUser); + COMPARE_BITMAPSET_FIELD(selectedCols); + COMPARE_BITMAPSET_FIELD(modifiedCols); ++ COMPARE_SCALAR_FIELD(rowlvPerms); + + return true; + } +@@ -2657,6 +2671,9 @@ equal(void *a, void *b) + case T_AlterOwnerStmt: + retval = _equalAlterOwnerStmt(a, b); + break; ++ case T_AlterSecLabelStmt: ++ retval = _equalAlterSecLabelStmt(a, b); ++ break; + case T_RuleStmt: + retval = _equalRuleStmt(a, b); + break; +diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c +index e7dae4b..4e8b350 100644 +--- a/src/backend/nodes/outfuncs.c ++++ b/src/backend/nodes/outfuncs.c +@@ -2135,6 +2135,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) + WRITE_OID_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(modifiedCols); ++ WRITE_UINT_FIELD(rowlvPerms); + } + + static void +diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c +index bc6e2a6..792b9ff 100644 +--- a/src/backend/nodes/readfuncs.c ++++ b/src/backend/nodes/readfuncs.c +@@ -1171,6 +1171,7 @@ _readRangeTblEntry(void) + READ_OID_FIELD(checkAsUser); + READ_BITMAPSET_FIELD(selectedCols); + READ_BITMAPSET_FIELD(modifiedCols); ++ READ_UINT_FIELD(rowlvPerms); + + READ_DONE(); + } +diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c +index a0e31a2..086b313 100644 +--- a/src/backend/optimizer/plan/createplan.c ++++ b/src/backend/optimizer/plan/createplan.c +@@ -32,6 +32,7 @@ + #include "optimizer/var.h" + #include "parser/parse_clause.h" + #include "parser/parsetree.h" ++#include "sepgsql/hooks.h" + #include "utils/lsyscache.h" + + +@@ -305,6 +306,9 @@ create_scan_plan(PlannerInfo *root, Path *best_path) + break; + } + ++ /* Append row-level access control policy */ ++ sepgsql_rowlv_add_policy(root, (Scan *)plan); ++ + /* + * If there are any pseudoconstant clauses attached to this node, insert a + * gating Result node that evaluates the pseudoconstants as one-time +diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c +index e525ba6..6474fbc 100644 +--- a/src/backend/optimizer/util/clauses.c ++++ b/src/backend/optimizer/util/clauses.c +@@ -38,6 +38,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_func.h" + #include "rewrite/rewriteManip.h" ++#include "sepgsql/hooks.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -3712,6 +3713,10 @@ inline_function(Oid funcid, Oid result_type, List *args, + if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) + return NULL; + ++ /* SELinux checks */ ++ if (!sepgsql_proc_be_inlined(func_tuple)) ++ return NULL; ++ + /* + * Make a temporary memory context, so that we don't leak all the stuff + * that parsing might create. +@@ -4164,7 +4169,8 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) + funcform->provolatile == PROVOLATILE_VOLATILE || + funcform->prosecdef || + !funcform->proretset || +- !heap_attisnull(func_tuple, Anum_pg_proc_proconfig)) ++ !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || ++ !sepgsql_proc_be_inlined(func_tuple)) + { + ReleaseSysCache(func_tuple); + return NULL; +diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c +index 6b99a10..4eb9d24 100644 +--- a/src/backend/parser/analyze.c ++++ b/src/backend/parser/analyze.c +@@ -25,6 +25,7 @@ + #include "postgres.h" + + #include "access/sysattr.h" ++#include "catalog/heap.h" + #include "catalog/pg_type.h" + #include "nodes/makefuncs.h" + #include "nodes/nodeFuncs.h" +@@ -40,6 +41,7 @@ + #include "parser/parse_target.h" + #include "parser/parsetree.h" + #include "rewrite/rewriteManip.h" ++#include "utils/guc.h" + #include "utils/rel.h" + + +@@ -651,7 +653,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) + tle = makeTargetEntry(expr, + attr_num, + col->name, +- false); ++ attr_num < 0 ? true : false); + qry->targetList = lappend(qry->targetList, tle); + + rte->modifiedCols = bms_add_member(rte->modifiedCols, +diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y +index b793c4d..866e0ba 100644 +--- a/src/backend/parser/gram.y ++++ b/src/backend/parser/gram.y +@@ -183,8 +183,8 @@ static TypeName *TableFuncTypeName(List *columns); + %type stmt schema_stmt + AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt + AlterForeignServerStmt AlterGroupStmt +- AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt +- AlterUserStmt AlterUserMappingStmt AlterUserSetStmt ++ AlterObjectSchemaStmt AlterOwnerStmt AlterSecLabelStmt AlterSeqStmt ++ AlterTableStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt + AlterRoleStmt AlterRoleSetStmt + AlterDefaultPrivilegesStmt DefACLAction + AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt +@@ -422,6 +422,8 @@ static TypeName *TableFuncTypeName(List *columns); + %type OptTableSpace OptConsTableSpace OptTableSpaceOwner + %type opt_check_option + ++%type SecLabelItem ++ + %type xml_attribute_el + %type xml_attribute_list xml_attributes + %type xml_root_version opt_xml_root_standalone +@@ -498,7 +500,7 @@ static TypeName *TableFuncTypeName(List *columns); + + KEY + +- LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING ++ LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING + LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP + LOCATION LOCK_P LOGIN_P + +@@ -654,6 +656,7 @@ stmt : + | AlterGroupStmt + | AlterObjectSchemaStmt + | AlterOwnerStmt ++ | AlterSecLabelStmt + | AlterSeqStmt + | AlterTableStmt + | AlterRoleSetStmt +@@ -1758,6 +1761,20 @@ alter_table_cmd: + n->subtype = AT_DropOids; + $$ = (Node *)n; + } ++ /* ALTER TABLE SET WITH SECURITY LABEL */ ++ | SET WITH SECURITY LABEL ++ { ++ AlterTableCmd *n = makeNode(AlterTableCmd); ++ n->subtype = AT_AddSecLabel; ++ $$ = (Node *)n; ++ } ++ /* ALTER TABLE SET WITHOUT SECURITY LABEL */ ++ | SET WITHOUT SECURITY LABEL ++ { ++ AlterTableCmd *n = makeNode(AlterTableCmd); ++ n->subtype = AT_DropSecLabel; ++ $$ = (Node *)n; ++ } + /* ALTER TABLE CLUSTER ON */ + | CLUSTER ON name + { +@@ -6022,6 +6039,102 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId + } + ; + ++/***************************************************************************** ++ * ++ * ALTER THING name SECURITY LABEL TO new_label ++ * ++ *****************************************************************************/ ++ ++AlterSecLabelStmt: ALTER DATABASE database_name SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_DATABASE; ++ n->object = list_make1(makeString($3)); ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ | ALTER SCHEMA name SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_SCHEMA; ++ n->object = list_make1(makeString($3)); ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ | ALTER TABLE relation_expr SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_TABLE; ++ n->relation = $3; ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ | ALTER TABLE relation_expr ALTER opt_column ColId SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_COLUMN; ++ n->relation = $3; ++ n->addname = $6; ++ n->secLabel = $7; ++ $$ = (Node *)n; ++ } ++ | ALTER SEQUENCE qualified_name SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_SEQUENCE; ++ n->relation = $3; ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ | ALTER VIEW qualified_name SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_VIEW; ++ n->relation = $3; ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ | ALTER FUNCTION function_with_argtypes SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_FUNCTION; ++ n->object = $3->funcname; ++ n->objarg = $3->funcargs; ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ | ALTER AGGREGATE func_name aggr_args SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_AGGREGATE; ++ n->object = $3; ++ n->objarg = $4; ++ n->secLabel = $5; ++ $$ = (Node *)n; ++ } ++ | ALTER LARGE_P OBJECT_P Iconst SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_LARGEOBJECT; ++ n->object = list_make1(makeInteger($4)); ++ n->secLabel = $5; ++ $$ = (Node *)n; ++ } ++ | ALTER TYPE_P any_name SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_TYPE; ++ n->object = $3; ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ ; ++ ++SecLabelItem: SECURITY LABEL TO Sconst ++ { ++ $$ = makeString($4); ++ } ++ ; + + /***************************************************************************** + * +@@ -10921,6 +11034,7 @@ unreserved_keyword: + | INVOKER + | ISOLATION + | KEY ++ | LABEL + | LANGUAGE + | LARGE_P + | LAST_P +diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c +index eb6505a..1962055 100644 +--- a/src/backend/parser/parse_relation.c ++++ b/src/backend/parser/parse_relation.c +@@ -2302,8 +2302,8 @@ specialAttNum(const char *attname) + { + Form_pg_attribute sysatt; + +- sysatt = SystemAttributeByName(attname, +- true /* "oid" will be accepted */ ); ++ /* "oid" and "security_label" will be accepted */ ++ sysatt = SystemAttributeByName(attname, true, true); + if (sysatt != NULL) + return sysatt->attnum; + return InvalidAttrNumber; +@@ -2324,7 +2324,9 @@ attnumAttName(Relation rd, int attid) + { + Form_pg_attribute sysatt; + +- sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids); ++ sysatt = SystemAttributeDefinition(attid, ++ rd->rd_rel->relhasoids, ++ rd->rd_rel->relhassecids); + return &sysatt->attname; + } + if (attid > rd->rd_att->natts) +@@ -2346,7 +2348,9 @@ attnumTypeId(Relation rd, int attid) + { + Form_pg_attribute sysatt; + +- sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids); ++ sysatt = SystemAttributeDefinition(attid, ++ rd->rd_rel->relhasoids, ++ rd->rd_rel->relhassecids); + return sysatt->atttypid; + } + if (attid > rd->rd_att->natts) +diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c +index e542dc0..8191f94 100644 +--- a/src/backend/parser/parse_target.c ++++ b/src/backend/parser/parse_target.c +@@ -14,6 +14,7 @@ + */ + #include "postgres.h" + ++#include "catalog/heap.h" + #include "catalog/pg_type.h" + #include "commands/dbcommands.h" + #include "funcapi.h" +@@ -365,16 +366,34 @@ transformAssignedExpr(ParseState *pstate, + Oid attrtype; /* type of target column */ + int32 attrtypmod; + Relation rd = pstate->p_target_relation; ++ bool relhasoids = RelationGetForm(rd)->relhasoids; ++ bool relhassecids = RelationGetForm(rd)->relhassecids; + + Assert(rd != NULL); +- if (attrno <= 0) +- ereport(ERROR, +- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), +- errmsg("cannot assign to system column \"%s\"", +- colname), +- parser_errposition(pstate, location))); +- attrtype = attnumTypeId(rd, attrno); +- attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ++ if (attrno > 0) ++ { ++ attrtype = attnumTypeId(rd, attrno); ++ attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ++ } ++ else ++ { ++ Form_pg_attribute attr; ++ ++ attr = SystemAttributeDefinition(attrno, relhasoids, relhassecids); ++ if (attr && SystemAttributeWritable(attrno, relhasoids, relhassecids)) ++ { ++ attrtype = attr->atttypid; ++ attrtypmod = attr->atttypmod; ++ } ++ else ++ { ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("cannot assign to system column \"%s\"", colname), ++ parser_errposition(pstate, location))); ++ return NULL; ++ } ++ } + + /* + * If the expression is a DEFAULT placeholder, insert the attribute's +@@ -503,6 +522,10 @@ updateTargetListEntry(ParseState *pstate, + List *indirection, + int location) + { ++ Relation rel = pstate->p_target_relation; ++ bool relhasoids = RelationGetForm(rel)->relhasoids; ++ bool relhassecids = RelationGetForm(rel)->relhassecids; ++ + /* Fix up expression as needed */ + tle->expr = transformAssignedExpr(pstate, + tle->expr, +@@ -519,6 +542,9 @@ updateTargetListEntry(ParseState *pstate, + */ + tle->resno = (AttrNumber) attrno; + tle->resname = colname; ++ ++ if (SystemAttributeWritable(attrno, relhasoids, relhassecids)) ++ tle->resjunk = true; + } + + +@@ -793,6 +819,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) + Bitmapset *wholecols = NULL; + Bitmapset *partialcols = NULL; + ListCell *tl; ++ uint32 system_attrs = 0UL; + + foreach(tl, cols) + { +@@ -801,14 +828,42 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) + int attrno; + + /* Lookup column name, ereport on failure */ +- attrno = attnameAttNum(pstate->p_target_relation, name, false); ++ attrno = attnameAttNum(pstate->p_target_relation, name, true); + if (attrno == InvalidAttrNumber) ++ { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + name, + RelationGetRelationName(pstate->p_target_relation)), + parser_errposition(pstate, col->location))); ++ } ++ else if (attrno < 0) ++ { ++ Relation rel = pstate->p_target_relation; ++ bool relhasoids = RelationGetForm(rel)->relhasoids; ++ bool relhassecids = RelationGetForm(rel)->relhassecids; ++ ++ if (SystemAttributeWritable(attrno, relhasoids, relhassecids)) ++ { ++ uint32 mask = (1<<(-attrno)); ++ ++ if ((system_attrs & mask) != 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_DUPLICATE_COLUMN), ++ errmsg("column \"%s\" specified more than once", ++ name), ++ parser_errposition(pstate, col->location))); ++ system_attrs |= mask; ++ *attrnos = lappend_int(*attrnos, attrno); ++ continue; ++ } ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), ++ errmsg("column \"%s\" of relation \"%s\" is system column", ++ name, RelationGetRelationName(rel)), ++ parser_errposition(pstate, col->location))); ++ } + + /* + * Check for duplicates, but only of whole columns --- we allow +@@ -1263,7 +1318,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) + expandRTE(rte, var->varno, 0, var->location, false, + &names, &vars); + +- tupleDesc = CreateTemplateTupleDesc(list_length(vars), false); ++ tupleDesc = CreateTemplateTupleDesc(list_length(vars), false, false); + i = 1; + forboth(lname, names, lvar, vars) + { +diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c +index 90d5c76..b78f9ba 100644 +--- a/src/backend/parser/parse_utilcmd.c ++++ b/src/backend/parser/parse_utilcmd.c +@@ -53,8 +53,10 @@ + #include "parser/parse_utilcmd.h" + #include "parser/parser.h" + #include "rewrite/rewriteManip.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" ++#include "utils/guc.h" + #include "utils/lsyscache.h" + #include "utils/relcache.h" + #include "utils/syscache.h" +@@ -70,6 +72,7 @@ typedef struct + List *inhRelations; /* relations to inherit from */ + bool isalter; /* true if altering existing table */ + bool hasoids; /* does relation have an OID column? */ ++ bool hassecids; /* does relation have an security label? */ + List *columns; /* ColumnDef items */ + List *ckconstraints; /* CHECK constraints */ + List *fkconstraints; /* FOREIGN KEY constraints */ +@@ -185,6 +188,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) + cxt.alist = NIL; + cxt.pkey = NULL; + cxt.hasoids = interpretOidsOption(stmt->options); ++ cxt.hassecids = default_with_secids; + + Assert(!stmt->ofTypename || !stmt->inhRelations); /* grammar enforces */ + +@@ -587,6 +591,9 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(relation)); + ++ /* SELinux checks */ ++ sepgsql_relation_getattr(RelationGetRelid(relation)); ++ + tupleDesc = RelationGetDescr(relation); + constr = tupleDesc->constr; + +@@ -1368,7 +1375,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) + if (constraint->contype == CONSTR_PRIMARY) + column->is_not_null = TRUE; + } +- else if (SystemAttributeByName(key, cxt->hasoids) != NULL) ++ else if (SystemAttributeByName(key, cxt->hasoids, cxt->hassecids) != NULL) + { + /* + * column will be a system column in the new table, so accept it. +@@ -1945,6 +1952,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) + cxt.inhRelations = NIL; + cxt.isalter = true; + cxt.hasoids = false; /* need not be right */ ++ cxt.hassecids = false; /* need not be right */ + cxt.columns = NIL; + cxt.ckconstraints = NIL; + cxt.fkconstraints = NIL; +diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c +index 98ab484..44c7a56 100644 +--- a/src/backend/postmaster/postmaster.c ++++ b/src/backend/postmaster/postmaster.c +@@ -109,6 +109,7 @@ + #include "postmaster/postmaster.h" + #include "postmaster/syslogger.h" + #include "replication/walsender.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "storage/ipc.h" + #include "storage/pg_shmem.h" +@@ -212,7 +213,8 @@ static pid_t StartupPID = 0, + AutoVacPID = 0, + PgArchPID = 0, + PgStatPID = 0, +- SysLoggerPID = 0; ++ SysLoggerPID = 0, ++ SecWorkerPID = 0; + + /* Startup/shutdown state */ + #define NoShutdown 0 +@@ -466,6 +468,7 @@ static void ShmemBackendArrayRemove(Backend *bn); + #define StartBackgroundWriter() StartChildProcess(BgWriterProcess) + #define StartWalWriter() StartChildProcess(WalWriterProcess) + #define StartWalReceiver() StartChildProcess(WalReceiverProcess) ++#define StartSecurityWorker() StartChildProcess(SecurityWorkerProcess) + + /* Macros to check exit status of a child process */ + #define EXIT_STATUS_0(st) ((st) == 0) +@@ -1473,6 +1476,11 @@ ServerLoop(void) + if (PgStatPID == 0 && pmState == PM_RUN) + PgStatPID = pgstat_start(); + ++ /* If we have lost security worker, try to start a new one */ ++ if (SecWorkerPID == 0 && pmState == PM_RUN && ++ sepgsql_worker_needed()) ++ SecWorkerPID = StartSecurityWorker(); ++ + /* If we need to signal the autovacuum launcher, do so now */ + if (avlauncher_needs_signal) + { +@@ -2113,6 +2121,8 @@ SIGHUP_handler(SIGNAL_ARGS) + signal_child(SysLoggerPID, SIGHUP); + if (PgStatPID != 0) + signal_child(PgStatPID, SIGHUP); ++ if (SecWorkerPID != 0) ++ signal_child(SecWorkerPID, SIGHUP); + + /* Reload authentication config files too */ + if (!load_hba()) +@@ -2173,6 +2183,9 @@ pmdie(SIGNAL_ARGS) + /* and the walwriter too */ + if (WalWriterPID != 0) + signal_child(WalWriterPID, SIGTERM); ++ /* and the security worker too */ ++ if (SecWorkerPID != 0) ++ signal_child(SecWorkerPID, SIGTERM); + pmState = PM_WAIT_BACKUP; + } + +@@ -2223,6 +2236,9 @@ pmdie(SIGNAL_ARGS) + /* and the walwriter too */ + if (WalWriterPID != 0) + signal_child(WalWriterPID, SIGTERM); ++ /* and the security worker too */ ++ if (SecWorkerPID != 0) ++ signal_child(SecWorkerPID, SIGTERM); + pmState = PM_WAIT_BACKENDS; + } + +@@ -2258,6 +2274,8 @@ pmdie(SIGNAL_ARGS) + signal_child(PgArchPID, SIGQUIT); + if (PgStatPID != 0) + signal_child(PgStatPID, SIGQUIT); ++ if (SecWorkerPID != 0) ++ signal_child(SecWorkerPID, SIGQUIT); + ExitPostmaster(0); + break; + } +@@ -2529,6 +2547,16 @@ reaper(SIGNAL_ARGS) + continue; + } + ++ /* Was it the security worker process? */ ++ if (pid == SecWorkerPID) ++ { ++ SecWorkerPID = 0; ++ if (!EXIT_STATUS_0(exitstatus)) ++ LogChildExit(LOG, _("security worker process"), ++ pid, exitstatus); ++ continue; ++ } ++ + /* + * Else do standard backend child cleanup. + */ +@@ -2732,6 +2760,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) + signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); + } + ++ /* Take care of the security worker process too */ ++ if (pid == SecWorkerPID) ++ SecWorkerPID = 0; ++ else if (SecWorkerPID != 0 && !FatalError) ++ { ++ ereport(DEBUG2, ++ (errmsg_internal("sending %s to process %d", ++ (SendStop ? "SIGSTOP" : "SIGQUIT"), ++ (int) SecWorkerPID))); ++ signal_child(SecWorkerPID, (SendStop ? SIGSTOP : SIGQUIT)); ++ } ++ + /* + * Force a power-cycle of the pgarch process too. (This isn't absolutely + * necessary, but it seems like a good idea for robustness, and it +@@ -2867,7 +2907,8 @@ PostmasterStateMachine(void) + WalReceiverPID == 0 && + (BgWriterPID == 0 || !FatalError) && + WalWriterPID == 0 && +- AutoVacPID == 0) ++ AutoVacPID == 0 && ++ SecWorkerPID == 0) + { + if (FatalError) + { +diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c +index 68be146..6ba1d2f 100644 +--- a/src/backend/rewrite/rewriteDefine.c ++++ b/src/backend/rewrite/rewriteDefine.c +@@ -27,6 +27,7 @@ + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteManip.h" + #include "rewrite/rewriteSupport.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/inval.h" +@@ -265,6 +266,9 @@ DefineQueryRewrite(char *rulename, + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(event_relation)); + ++ /* SELinux checks */ ++ sepgsql_rule_create(event_relid, rulename); ++ + /* + * No rule actions that modify OLD or NEW + */ +diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c +index 25b44dd..e35f7c5 100644 +--- a/src/backend/rewrite/rewriteHandler.c ++++ b/src/backend/rewrite/rewriteHandler.c +@@ -23,6 +23,7 @@ + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteHandler.h" + #include "rewrite/rewriteManip.h" ++#include "sepgsql/hooks.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" + #include "commands/trigger.h" +@@ -1938,6 +1939,7 @@ QueryRewrite(Query *parsetree) + foreach(l, results) + { + Query *query = (Query *) lfirst(l); ++ ListCell *cell; + + if (query->querySource == QSRC_ORIGINAL) + { +@@ -1956,7 +1958,16 @@ QueryRewrite(Query *parsetree) + query->querySource == QSRC_QUAL_INSTEAD_RULE)) + lastInstead = query; + } ++ ++ /* Fixup row-level access control permissions */ ++ foreach (cell, query->rtable) ++ { ++ RangeTblEntry *rte = lfirst(cell); ++ ++ rte->rowlvPerms = sepgsql_rowlv_permissions(rte); ++ } + } ++ sepgsql_proxy_queries(results); + + if (!foundOriginalQuery && lastInstead != NULL) + lastInstead->canSetTag = true; +diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c +index c1c5ce9..0f6a446 100644 +--- a/src/backend/rewrite/rewriteRemove.c ++++ b/src/backend/rewrite/rewriteRemove.c +@@ -22,6 +22,7 @@ + #include "catalog/pg_rewrite.h" + #include "miscadmin.h" + #include "rewrite/rewriteRemove.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/fmgroids.h" + #include "utils/inval.h" +@@ -77,6 +78,9 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(eventRelationOid)); + ++ /* SELinux checks */ ++ sepgsql_rule_drop(eventRelationOid, ruleName, false); ++ + /* + * Do the deletion + */ +diff --git a/src/backend/sepgsql/Makefile b/src/backend/sepgsql/Makefile +new file mode 100644 +index 0000000..f7119da +--- /dev/null ++++ b/src/backend/sepgsql/Makefile +@@ -0,0 +1,21 @@ ++# ++# Makefile for security subsystem ++# ++ ++subdir = src/backend/sepgsql ++top_builddir = ../../.. ++include $(top_builddir)/src/Makefile.global ++ ++ifeq ($(enable_selinux), yes) ++OBJS = selinux.o avc.o label.o ++else ++OBJS = dummy.o ++endif ++ ++OBJS += proxy.o rowlv.o ++ ++OBJS += misc.o database.o schema.o relation.o attribute.o proc.o \ ++ type.o tablespace.o operator.o role.o blob.o conversion.o \ ++ tsearch.o fdw.o ++ ++include $(top_srcdir)/src/backend/common.mk +diff --git a/src/backend/sepgsql/attribute.c b/src/backend/sepgsql/attribute.c +new file mode 100644 +index 0000000..3b9c916 +--- /dev/null ++++ b/src/backend/sepgsql/attribute.c +@@ -0,0 +1,260 @@ ++/* ++ * attribute.c ++ * ++ * SELinux hooks related to attribute ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/heapam.h" ++#include "access/sysattr.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_class.h" ++#include "catalog/pg_seclabel.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_attribute_common(Oid relOid, AttrNumber attno, ++ uint32 required, bool abort) ++{ ++ Form_pg_attribute attForm; ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ char auname[NAMEDATALEN * 2 + 10]; ++ bool retval; ++ ++ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); ++ ++ tuple = SearchSysCache2(ATTNUM, ++ ObjectIdGetDatum(relOid), ++ Int16GetDatum(attno)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for attribute %d of relation %u", ++ attno, relOid); ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ ++ tsid.relid = AttributeRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ snprintf(auname, sizeof(auname), "%s.%s", ++ get_rel_name(relOid), NameStr(attForm->attname)); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_COLUMN, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_attribute_create(Oid relOid, const char *attName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char auname[NAMEDATALEN * 2 + 10]; ++ ++ if (get_rel_relkind(relOid) != RELKIND_RELATION) ++ { ++ sepgsql_relation_common(relOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ nsid.relid = RelationRelationId; ++ nsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); ++ ++ nsid = sepgsql_move_secid(AttributeRelationId, nsid); ++ ++ return nsid.secid; ++ } ++ ++ nsid = sepgsql_get_default_column_secid(relOid); ++ snprintf(auname, sizeof(auname), "%s.%s", ++ get_rel_name(relOid), attName); ++ ++ /* db_column:{create} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_COLUMN, ++ SEPG_DB_COLUMN__CREATE, ++ auname, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_attribute_alter(Oid relOid, const char *attName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ AttrNumber attnum = get_attnum(relOid, attName); ++ ++ if (attnum == InvalidAttrNumber) ++ return; /* to be failed later */ ++ ++ if (get_rel_relkind(relOid) == RELKIND_RELATION) ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__SETATTR, true); ++ else ++ sepgsql_relation_common(relOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_attribute_relabel(Oid relOid, const char *attName, char *new_label) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ AttrNumber attnum = get_attnum(relOid, attName); ++ sepgsql_sid_t nsid; ++ char auname[NAMEDATALEN * 2 + 10]; ++ ++ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); ++ if (attnum == InvalidAttrNumber) ++ return InvalidOid; /* to be failed later */ ++ ++ nsid.relid = AttributeRelationId; ++ nsid.secid = seclabelTransInput(nsid.relid, new_label); ++ ++ snprintf(auname, sizeof(auname), "%s.%s", ++ get_rel_name(relOid), get_attname(relOid, attnum)); ++ ++ /* db_column:{setattr relabelfrom} */ ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__SETATTR | ++ SEPG_DB_COLUMN__RELABELFROM, ++ true); ++ ++ /* db_column:{relabelto} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_COLUMN, ++ SEPG_DB_COLUMN__RELABELTO, ++ auname, ++ true); ++ return nsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_attribute_drop(Oid relOid, const char *attName, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ AttrNumber attnum = get_attnum(relOid, attName); ++ ++ /* ++ * If not found, the caller raises an error with an appropriate ++ * error message. ++ */ ++ if (attnum == InvalidAttrNumber) ++ return; ++ ++ if (get_rel_relkind(relOid) == RELKIND_RELATION) ++ { ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__DROP, true); ++ /* ++ * ALTER TABLE SET WITHOUT SECURITY LABEL is equivalent to ++ * relabel all the tuples within the target relation. ++ * In this case, we need to check {relabelfrom relabelto} ++ */ ++ if (!cascade && ++ attnum == SecurityLabelAttributeNumber) ++ { ++ Relation rel; ++ HeapScanDesc scan; ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ ++ rel = heap_open(relOid, AccessShareLock); ++ ++ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); ++ ++ while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) ++ { ++ /* db_tuple:{update relabelfrom} */ ++ tsid.relid = relOid; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__UPDATE | ++ SEPG_DB_TUPLE__RELABELFROM, ++ NULL, ++ true); ++ } ++ heap_endscan(scan); ++ ++ heap_close(rel, AccessShareLock); ++ ++ /* db_tuple:{relabelto} */ ++ tsid.relid = RelationRelationId; ++ tsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); ++ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELTO, ++ NULL, ++ true); ++ } ++ } ++ else if (!cascade) ++ sepgsql_relation_common(relOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_attribute_grant(Oid relOid, AttrNumber attnum) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ if (get_rel_relkind(relOid) == RELKIND_RELATION) ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__SETATTR, true); ++ else ++ sepgsql_relation_common(relOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_attribute_comment(Oid relOid, AttrNumber attnum) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ if (get_rel_relkind(relOid) == RELKIND_RELATION) ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__SETATTR, true); ++ else ++ sepgsql_relation_common(relOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/avc.c b/src/backend/sepgsql/avc.c +new file mode 100644 +index 0000000..5f58974 +--- /dev/null ++++ b/src/backend/sepgsql/avc.c +@@ -0,0 +1,503 @@ ++/* ++ * avc.c ++ * userspace access vector cache ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/hash.h" ++#include "access/xact.h" ++#include "catalog/pg_seclabel.h" ++#include "libpq/libpq-be.h" ++#include "libpq/pqsignal.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "storage/shmem.h" ++#include "storage/lock.h" ++#include "utils/memutils.h" ++ ++#include ++#include ++#include ++ ++/* ------------------------------------------------------------ ++ * ++ * Userspace Access Vector Cache ++ * ++ * ------------------------------------------------------------ ++ */ ++static MemoryContext AvcMemCtx = NULL; ++ ++#define AVC_HASH_NUM_SLOTS 256 ++#define AVC_HASH_NUM_NODES 180 ++ ++#define avc_hash_key(trelid, tsecid, tclass, nrelid) \ ++ (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3) ^ (nrelid))) ++ ++typedef struct _avc_datum ++{ ++ uint32 hash_key; ++ ++ uint16 tclass; ++ sepgsql_sid_t tsid; ++ sepgsql_sid_t nsid; ++ char *tcontext; ++ char *ncontext; ++ ++ uint32 allowed; ++ uint32 auditallow; ++ uint32 auditdeny; ++ bool permissive; ++ ++ bool hot_cache; ++} avc_datum; ++ ++typedef struct _avc_page ++{ ++ struct _avc_page *next; ++ ++ List *slot[AVC_HASH_NUM_SLOTS]; ++ ++ uint32 avc_count; ++ uint32 lru_hint; ++ ++ char scontext[1]; ++} avc_page; ++ ++static avc_page *current_page = NULL; ++ ++static int avc_version = -1; ++ ++/* ++ * selinux_state ++ * ++ * It is deployed on the shared memory region, to show the system ++ * state of SELinux and its security policy. ++ */ ++struct ++{ ++ int version; ++ ++ bool enforcing; ++} *selinux_state = NULL; ++ ++Size ++sepgsql_shmem_size(void) ++{ ++ return sizeof(*selinux_state); ++} ++ ++static void ++sepgsql_shmem_init(void) ++{ ++ bool found; ++ ++ selinux_state = ShmemInitStruct("SELinux system state", ++ sepgsql_shmem_size(), &found); ++ if (!found) ++ { ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ ++ selinux_state->version = 0; ++ selinux_state->enforcing = (security_getenforce() > 0); ++ ++ LWLockRelease(SepgsqlAvcLock); ++ } ++} ++ ++void ++sepgsql_avc_switch(const char *scontext) ++{ ++ avc_page *new_page; ++ int i, length; ++ ++ if (current_page) ++ { ++ new_page = current_page; ++ do { ++ if (strcmp(new_page->scontext, scontext) == 0) ++ { ++ current_page = new_page; ++ return; ++ } ++ new_page = new_page->next; ++ } while (new_page != current_page); ++ } ++ ++ /* ++ * Not found, so create a new avc_page ++ */ ++ length = sizeof(avc_page) + strlen(scontext); ++ new_page = MemoryContextAllocZero(AvcMemCtx, length); ++ ++ strcpy(new_page->scontext, scontext); ++ for (i = 0; i < AVC_HASH_NUM_SLOTS; i++) ++ new_page->slot[i] = NIL; ++ ++ if (!current_page) ++ new_page->next = new_page; ++ else ++ { ++ new_page->next = current_page->next; ++ current_page->next = new_page; ++ } ++ current_page = new_page; ++} ++ ++ ++static void ++sepgsql_avc_reset(void) ++{ ++ Assert(AvcMemCtx != NULL); ++ ++ MemoryContextReset(AvcMemCtx); ++ ++ current_page = NULL; ++ ++ sepgsql_avc_switch(sepgsql_get_client_label()); ++} ++ ++static bool ++sepgsql_avc_is_valid(void) ++{ ++ bool result = true; ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); ++ if (avc_version != selinux_state->version) ++ { ++ sepgsql_avc_reset(); ++ ++ /* copy current version to local variable */ ++ avc_version = selinux_state->version; ++ ++ result = false; ++ } ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return result; ++} ++ ++ ++static void ++sepgsql_avc_reclaim(avc_page *page) ++{ ++ ListCell *l; ++ ++ while (page->avc_count > AVC_HASH_NUM_NODES - 10) ++ { ++ foreach (l, page->slot[page->lru_hint]) ++ { ++ avc_datum *cache = lfirst(l); ++ ++ if (cache->hot_cache) ++ cache->hot_cache = false; ++ { ++ list_delete_ptr(page->slot[page->lru_hint], cache); ++ pfree(cache); ++ page->avc_count--; ++ } ++ } ++ page->lru_hint = (page->lru_hint + 1) % AVC_HASH_NUM_SLOTS; ++ } ++} ++ ++static avc_datum * ++sepgsql_avc_make_entry(avc_page *page, ++ sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) ++{ ++ struct av_decision avd; ++ MemoryContext oldctx; ++ char *scontext; ++ char *tcontext; ++ char *ncontext; ++ avc_datum *cache; ++ uint32 hash_key, index; ++ ++ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid); ++ index = hash_key % AVC_HASH_NUM_SLOTS; ++ ++ oldctx = MemoryContextSwitchTo(AvcMemCtx); ++ ++ scontext = page->scontext; ++ tcontext = seclabelRawOutput(tsid.relid, tsid.secid); ++ ncontext = sepgsql_compute_create(scontext, tcontext, tclass); ++ ++ sepgsql_compute_avd(scontext, tcontext, tclass, &avd); ++ ++ cache = palloc0(sizeof(avc_datum)); ++ ++ cache->hash_key = hash_key; ++ ++ cache->tclass = tclass; ++ ++ cache->hot_cache = true; ++ cache->tcontext = tcontext; ++ cache->ncontext = ncontext; ++ ++ cache->tsid.relid = tsid.relid; ++ cache->tsid.secid = tsid.secid; ++ ++ if (OidIsValid(nrelid)) ++ { ++ cache->nsid.relid = nrelid; ++ cache->nsid.secid = seclabelRawInput(nrelid, ncontext); ++ } ++ ++ cache->allowed = avd.allowed; ++ cache->auditallow = avd.auditallow; ++ cache->auditdeny = avd.auditdeny; ++ if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) ++ cache->permissive = true; ++ ++ if (page->avc_count > AVC_HASH_NUM_NODES) ++ sepgsql_avc_reclaim(page); ++ ++ page->slot[index] = lcons(cache, page->slot[index]); ++ page->avc_count++; ++ ++ MemoryContextSwitchTo(oldctx); ++ ++ return cache; ++} ++ ++static avc_datum * ++sepgsql_avc_lookup(avc_page *page, ++ sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) ++{ ++ avc_datum *cache = NULL; ++ uint32 hash_key, index; ++ ListCell *l; ++ ++ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid); ++ index = hash_key % AVC_HASH_NUM_SLOTS; ++ ++ foreach (l, page->slot[index]) ++ { ++ cache = lfirst(l); ++ if (cache->hash_key == hash_key && ++ cache->tclass == tclass && ++ cache->tsid.relid == tsid.relid && ++ cache->tsid.secid == tsid.secid && ++ cache->nsid.relid == nrelid) ++ { ++ cache->hot_cache = true; ++ return cache; ++ } ++ } ++ return NULL; ++} ++ ++bool ++sepgsql_client_perms(sepgsql_sid_t tsid, ++ uint16 tclass, uint32 required, ++ const char *audit_name, bool abort) ++{ ++ avc_datum *cache; ++ uint32 denied, audited; ++ bool result = true; ++ ++ do { ++ cache = sepgsql_avc_lookup(current_page, ++ tsid, tclass, InvalidOid); ++ if (!cache) ++ cache = sepgsql_avc_make_entry(current_page, ++ tsid, tclass, InvalidOid); ++ } while (!sepgsql_avc_is_valid()); ++ ++ denied = required & ~cache->allowed; ++ if (sepgsql_debug_audit && tclass != SEPG_CLASS_DB_TUPLE) ++ audited = (denied ? (denied & ~0) : (required & ~0)); ++ else ++ audited = (denied ? (denied & cache->auditdeny) ++ : (required & cache->auditallow)); ++ ++ if (audited) ++ { ++ sepgsql_audit_log(!!denied, ++ current_page->scontext, ++ seclabelRawOutput(tsid.relid, tsid.secid), ++ tclass, audited, audit_name); ++ } ++ ++ if (denied) ++ { ++ if (!sepgsql_get_enforce() || cache->permissive) ++ cache->allowed |= required; /* prevent flood of audit log */ ++ else ++ { ++ if (abort) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("SELinux: security policy violation"))); ++ result = false; ++ } ++ } ++ ++ return result; ++} ++ ++sepgsql_sid_t ++sepgsql_client_create_secid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) ++{ ++ avc_datum *cache; ++ ++ do { ++ cache = sepgsql_avc_lookup(current_page, tsid, tclass, nrelid); ++ ++ if (!cache) ++ cache = sepgsql_avc_make_entry(current_page, ++ tsid, tclass, nrelid); ++ } while (!sepgsql_avc_is_valid()); ++ ++ return cache->nsid; ++} ++ ++char * ++sepgsql_client_create_label(sepgsql_sid_t tsid, uint16 tclass) ++{ ++ avc_datum *cache; ++ ++ do { ++ cache = sepgsql_avc_lookup(current_page, tsid, tclass, InvalidOid); ++ ++ if (!cache) ++ cache = sepgsql_avc_make_entry(current_page, ++ tsid, tclass, InvalidOid); ++ } while (!sepgsql_avc_is_valid()); ++ ++ return cache->ncontext; ++} ++ ++static void ++sepgsql_avc_xact_callback(XactEvent event, void *arg) ++{ ++ if (event == XACT_EVENT_ABORT) ++ sepgsql_avc_reset(); ++} ++ ++static void ++sepgsql_avc_sub_xact_callback(SubXactEvent event, SubTransactionId mySubid, ++ SubTransactionId parentSubid, void *arg) ++{ ++ if (event == SUBXACT_EVENT_ABORT_SUB) ++ sepgsql_avc_reset(); ++} ++ ++void ++sepgsql_avc_init(void) ++{ ++ sepgsql_shmem_init(); ++ ++ AvcMemCtx = AllocSetContextCreate(TopMemoryContext, ++ "Userspace AVC", ++ ALLOCSET_DEFAULT_MINSIZE, ++ ALLOCSET_DEFAULT_INITSIZE, ++ ALLOCSET_DEFAULT_MAXSIZE); ++ /* ++ * userspace avc should be invalidate when the current transaction ++ * is aborted on errors, because sid to be created shall be rollbacked. ++ */ ++ RegisterXactCallback(sepgsql_avc_xact_callback, NULL); ++ RegisterSubXactCallback(sepgsql_avc_sub_xact_callback, NULL); ++} ++ ++/* ------------------------------------------------------------ ++ * ++ * SELinux state monitor process ++ * ++ * ------------------------------------------------------------ ++ */ ++static int ++sepgsql_cb_log(int type, const char *fmt, ...) ++{ ++ char *c, buffer[1024]; ++ va_list ap; ++ ++ va_start(ap, fmt); ++ vsnprintf(buffer, sizeof(buffer), fmt, ap); ++ va_end(ap); ++ ++ c = strrchr(buffer, '\n'); ++ if (c) ++ *c = '\0'; ++ ++ ereport(LOG,(errmsg("%s", buffer))); ++ ++ return 0; ++} ++ ++static int ++sepgsql_cb_setenforce(int enforce) ++{ ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ selinux_state->enforcing = (enforce ? true : false); ++ selinux_state->version++; ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return 0; ++} ++ ++static int ++sepgsql_cb_policyload(int seqno) ++{ ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ selinux_state->version++; ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return 0; ++} ++ ++void ++sepgsql_avc_worker_main(void) ++{ ++ union selinux_callback cb; ++ ++ Assert(sepgsql_is_enabled()); ++ ++#ifdef HAVE_SETSID ++ if (setsid() < 0) ++ elog(FATAL, "setsid() failed: %m"); ++#endif ++ ++ /* ++ * setup the signal handler ++ */ ++ pqinitmask(); ++ pqsignal(SIGHUP, SIG_IGN); ++ pqsignal(SIGINT, SIG_IGN); ++ pqsignal(SIGTERM, exit); ++ pqsignal(SIGQUIT, exit); ++ pqsignal(SIGUSR1, SIG_IGN); ++ pqsignal(SIGUSR2, SIG_IGN); ++ pqsignal(SIGCHLD, SIG_DFL); ++ PG_SETMASK(&UnBlockSig); ++ ++ /* ++ * map shared memory segment ++ */ ++ sepgsql_shmem_init(); ++ ++ ereport(LOG, (errmsg("SELinux: netlink receiver (pid=%u)", getpid()))); ++ ++ /* ++ * setup callback functions from avc_netlink_loop() ++ */ ++ cb.func_log = sepgsql_cb_log; ++ selinux_set_callback(SELINUX_CB_LOG, cb); ++ cb.func_setenforce = sepgsql_cb_setenforce; ++ selinux_set_callback(SELINUX_CB_SETENFORCE, cb); ++ cb.func_policyload = sepgsql_cb_policyload; ++ selinux_set_callback(SELINUX_CB_POLICYLOAD, cb); ++ ++ /* ++ * open netlink socket and wait for messages ++ */ ++ avc_netlink_open(1); ++ ++ avc_netlink_loop(); ++ ++ exit(0); ++} +diff --git a/src/backend/sepgsql/blob.c b/src/backend/sepgsql/blob.c +new file mode 100644 +index 0000000..e60e9cc +--- /dev/null ++++ b/src/backend/sepgsql/blob.c +@@ -0,0 +1,245 @@ ++/* ++ * blob.c ++ * ++ * SELinux hooks related to large objects ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/genam.h" ++#include "access/heapam.h" ++#include "access/sysattr.h" ++#include "catalog/indexing.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_largeobject_metadata.h" ++#include "catalog/pg_seclabel.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/fmgroids.h" ++#include "utils/tqual.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_largeobject_common(Oid loid, Snapshot snapshot, ++ uint32 required, bool abort) ++{ ++ Relation pg_lo_meta; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ char auname[NAMEDATALEN]; ++ bool retval; ++ ++ snprintf(auname, sizeof(auname), "blob_%u", loid); ++ ++ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, ++ AccessShareLock); ++ ++ ScanKeyInit(&skey, ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(loid)); ++ ++ scan = systable_beginscan(pg_lo_meta, ++ LargeObjectMetadataOidIndexId, true, ++ snapshot, 1, &skey); ++ ++ tuple = systable_getnext(scan); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "large object %u does not exist", loid); ++ ++ tsid.relid = LargeObjectMetadataRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_BLOB, ++ required, ++ auname, ++ abort); ++ systable_endscan(scan); ++ ++ heap_close(pg_lo_meta, AccessShareLock); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_largeobject_create(Oid loid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char auname[NAMEDATALEN]; ++ ++ nsid = sepgsql_get_default_blob_secid(MyDatabaseId); ++ snprintf(auname, sizeof(auname), "blob_%u", loid); ++ ++ /* db_blob:{create} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__CREATE, ++ auname, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_largeobject_alter(Oid loid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_largeobject_common(loid, SnapshotNow, ++ SEPG_DB_BLOB__SETATTR, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_largeobject_relabel(Oid loid, char *newLabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char auname[NAMEDATALEN]; ++ ++ nsid.relid = LargeObjectMetadataRelationId; ++ nsid.secid = seclabelTransInput(nsid.relid, newLabel); ++ ++ snprintf(auname, sizeof(auname), "blob_%u", loid); ++ ++ /* db_blob:{setattr relabelfrom} */ ++ sepgsql_largeobject_common(loid, ++ SnapshotNow, ++ SEPG_DB_BLOB__SETATTR | ++ SEPG_DB_BLOB__RELABELFROM, ++ true); ++ /* db_blob:{relabelto} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__RELABELTO, ++ auname, ++ true); ++ return nsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_largeobject_drop(Oid loid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_largeobject_common(loid, SnapshotNow, ++ SEPG_DB_BLOB__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_largeobject_read(Oid loid, Snapshot snapshot) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_largeobject_common(loid, snapshot, ++ SEPG_DB_BLOB__READ, true); ++ } ++#endif ++} ++ ++void ++sepgsql_largeobject_write(Oid loid, Snapshot snapshot) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_largeobject_common(loid, snapshot, ++ SEPG_DB_BLOB__WRITE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_largeobject_import(Oid loid, const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char auname[NAMEDATALEN]; ++ ++ nsid = sepgsql_get_default_blob_secid(MyDatabaseId); ++ snprintf(auname, sizeof(auname), "blob_%u", loid); ++ ++ /* db_blob:{create} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__CREATE | ++ SEPG_DB_BLOB__WRITE | ++ SEPG_DB_BLOB__IMPORT, ++ auname, ++ true); ++ /* db_file:{read} */ ++ // XXX - todo: add file read checks ++ ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_largeobject_export(Oid loid, Snapshot snapshot, const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_blob:{read export} */ ++ sepgsql_largeobject_common(loid, snapshot, ++ SEPG_DB_BLOB__READ | ++ SEPG_DB_BLOB__EXPORT, true); ++ /* file:{write} */ ++ // TODO: add security checks ++ } ++#endif ++} ++ ++void ++sepgsql_largeobject_grant(Oid loid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_largeobject_common(loid, SnapshotNow, ++ SEPG_DB_BLOB__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_largeobject_comment(Oid loid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_largeobject_common(loid, SnapshotNow, ++ SEPG_DB_BLOB__SETATTR, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/conversion.c b/src/backend/sepgsql/conversion.c +new file mode 100644 +index 0000000..6a60284 +--- /dev/null ++++ b/src/backend/sepgsql/conversion.c +@@ -0,0 +1,147 @@ ++/* ++ * conversion.c ++ * ++ * SELinux hooks related to conversion ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_conversion.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_conversion_common(Oid convOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(convOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for conversion %u", convOid); ++ ++ tsid.relid = ConversionRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_conversion_namespace(Oid convOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(convOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++#endif ++ ++Oid ++sepgsql_conversion_create(const char *convName, ++ Oid namespaceId, Oid conversionFunc) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ nsid = sepgsql_get_default_tuple_secid(ConversionRelationId); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME, true); ++ /* db_procedure:{install} */ ++ sepgsql_proc_common(conversionFunc, ++ SEPG_DB_PROCEDURE__INSTALL, true); ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ convName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_conversion_alter(Oid convOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_conversion_alter_rename(Oid convOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_conversion_namespace(convOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_conversion_drop(Oid convOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_conversion_namespace(convOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_conversion_comment(Oid convOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/database.c b/src/backend/sepgsql/database.c +new file mode 100644 +index 0000000..bee9d36 +--- /dev/null ++++ b/src/backend/sepgsql/database.c +@@ -0,0 +1,201 @@ ++/* ++ * database.c ++ * ++ * SELinux hooks related to database ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_database.h" ++#include "catalog/pg_seclabel.h" ++#include "commands/dbcommands.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_database_common(Oid datOid, uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(DATABASEOID, ++ ObjectIdGetDatum(datOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for database %u", datOid); ++ ++ tsid.relid = DatabaseRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_DATABASE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_database_create(const char *datName, Oid templateOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ /* compute a default security context */ ++ nsid = sepgsql_get_default_database_secid(templateOid); ++ ++ /* db_database:{create} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_DATABASE, ++ SEPG_DB_DATABASE__CREATE, ++ datName, true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_database_alter(Oid databaseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__SETATTR, ++ true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_database_relabel(Oid databaseOid, char *new_label) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char *auname; ++ ++ nsid.relid = DatabaseRelationId; ++ nsid.secid = seclabelTransInput(nsid.relid, new_label); ++ ++ auname = get_database_name(databaseOid); ++ ++ /* db_database:{setattr relabelfrom} */ ++ sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__SETATTR | ++ SEPG_DB_DATABASE__RELABELFROM, ++ true); ++ ++ /* db_database:{relabelto} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_DATABASE, ++ SEPG_DB_DATABASE__RELABELTO, ++ auname, ++ true); ++ pfree(auname); ++ ++ return nsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_database_drop(Oid databaseOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__DROP, ++ true); ++ } ++#endif ++} ++ ++void ++sepgsql_database_grant(Oid databaseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__SETATTR, ++ true); ++ } ++#endif ++} ++ ++void ++sepgsql_database_comment(Oid databaseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__SETATTR, ++ true); ++ } ++#endif ++} ++ ++void ++sepgsql_database_connect(Oid databaseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ if (!sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__ACCESS, ++ false)) ++ ereport(FATAL, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("permission denied for database \"%s\"", ++ get_database_name(databaseOid)))); ++ } ++#endif ++} ++ ++void ++sepgsql_database_reindex(Oid databaseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ // TODO: check db_table:{indexon} for each ++ ++ ++ ++ } ++#endif ++} ++ ++void ++sepgsql_database_getattr(Oid databaseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__GETATTR, ++ true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/fdw.c b/src/backend/sepgsql/fdw.c +new file mode 100644 +index 0000000..be4a34b +--- /dev/null ++++ b/src/backend/sepgsql/fdw.c +@@ -0,0 +1,296 @@ ++/* ++ * fdw.c ++ * ++ * SELinux hooks related to foreign data wrapper ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_foreign_data_wrapper.h" ++#include "catalog/pg_foreign_server.h" ++#include "catalog/pg_user_mapping.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort) ++{ ++ Form_pg_foreign_data_wrapper fdwForm; ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ bool retval; ++ ++ tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for foreign-data-wrapper %u", fdwOid); ++ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple); ++ ++ tsid.relid = ForeignDataWrapperRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ NameStr(fdwForm->fdwname), ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++bool ++sepgsql_fserver_common(Oid fservOid, uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fservOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for foreign-server %u", fservOid); ++ ++ tsid.relid = ForeignServerRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_foreign_server) GETSTRUCT(tuple))->srvname); ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static char * ++get_fserver_name(Oid fservOid) ++{ ++ Form_pg_foreign_server servForm; ++ HeapTuple tuple; ++ char *srvname = NULL; ++ ++ tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fservOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ servForm = (Form_pg_foreign_server) GETSTRUCT(tuple); ++ ++ srvname = pstrdup(NameStr(servForm->srvname)); ++ ++ ReleaseSysCache(tuple); ++ } ++ return srvname; ++} ++ ++bool ++sepgsql_user_mapping_common(Oid umapOid, uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ Oid umuser; ++ Oid umserver; ++ char auname[NAMEDATALEN * 2 + 10]; ++ bool retval; ++ ++ tuple = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umapOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for user mapping %u", umapOid); ++ ++ tsid.relid = UserMappingRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ umuser = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umuser; ++ umserver = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umserver; ++ ++ snprintf(auname, sizeof(auname), "%s@%s", ++ OidIsValid(umuser) ? GetUserNameFromId(umuser) : "public", ++ get_fserver_name(umserver)); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_fdw_create(const char *fdwName, Oid validatorFunc) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(ForeignDataWrapperRelationId); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(validatorFunc)) ++ sepgsql_proc_common(validatorFunc, ++ SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ fdwName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_fdw_alter(Oid fdwOid, Oid newValidator) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_procedure:{install} */ ++ if (OidIsValid(newValidator)) ++ sepgsql_proc_common(newValidator, ++ SEPG_DB_PROCEDURE__INSTALL, true); ++ /* db_tuple:{update} */ ++ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_fdw_drop(Oid fdwOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{delete} */ ++ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_fdw_grant(Oid fdwOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_fserver_create(const char *fservName, Oid fdwOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(ForeignServerRelationId); ++ ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ fservName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_fserver_alter(Oid fservOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_fserver_drop(Oid fservOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_fserver_grant(Oid fservOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_user_mapping_create(Oid umuserId, Oid fservOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ char auname[NAMEDATALEN * 2 + 10]; ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(UserMappingRelationId); ++ ++ snprintf(auname, sizeof(auname), "%s@%s", ++ OidIsValid(umuserId) ? GetUserNameFromId(umuserId) : "public", ++ get_fserver_name(fservOid)); ++ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ auname, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_user_mapping_alter(Oid umapOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_user_mapping_common(umapOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_user_mapping_drop(Oid umapOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{delete} */ ++ sepgsql_user_mapping_common(umapOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/label.c b/src/backend/sepgsql/label.c +new file mode 100644 +index 0000000..9a61902 +--- /dev/null ++++ b/src/backend/sepgsql/label.c +@@ -0,0 +1,656 @@ ++/* ++ * label.c ++ * SE-PostgreSQL security label management ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/relscan.h" ++#include "access/xact.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_class.h" ++#include "catalog/pg_database.h" ++#include "catalog/pg_largeobject_metadata.h" ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_seclabel.h" ++#include "catalog/pg_type.h" ++#include "commands/dbcommands.h" ++#include "miscadmin.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/fmgroids.h" ++#include "utils/lsyscache.h" ++#include "utils/rel.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++#include // for workaround hack ++#include ++ ++/* ++ * guc parameter to turn on/off mcstrans ++ */ ++bool sepgsql_mcstrans; ++ ++sepgsql_sid_t ++sepgsql_move_secid(Oid dst_relid, sepgsql_sid_t ssid) ++{ ++ char *label; ++ sepgsql_sid_t dsid = { .relid = dst_relid, ++ .secid = InvalidOid }; ++ ++ label = seclabelRawOutput(ssid.relid, ssid.secid); ++ if (label) ++ { ++ dsid.secid = seclabelRawInput(dsid.relid, label); ++ ++ pfree(label); ++ } ++ return dsid; ++} ++ ++static sepgsql_sid_t ++get_default_secid_with_database(Oid relOid, Oid databaseOid, uint16 tclass) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t datsid; ++ ++ tuple = SearchSysCache1(DATABASEOID, ++ ObjectIdGetDatum(databaseOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for database: %u", databaseOid); ++ ++ datsid.relid = DatabaseRelationId; ++ datsid.secid = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ ++ return sepgsql_client_create_secid(datsid, tclass, relOid); ++} ++ ++static sepgsql_sid_t ++get_default_secid_with_schema(Oid relOid, Oid namespaceOid, uint16 tclass) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t nspsid; ++ ++ tuple = SearchSysCache1(NAMESPACEOID, ++ ObjectIdGetDatum(namespaceOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for namespace: %u", namespaceOid); ++ ++ nspsid.relid = NamespaceRelationId; ++ nspsid.secid = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ ++ return sepgsql_client_create_secid(nspsid, tclass, relOid); ++} ++ ++static sepgsql_sid_t ++get_default_secid_with_table(Oid relOid, Oid tableOid, uint16 tclass) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tblsid; ++ ++ tuple = SearchSysCache1(RELOID, ++ ObjectIdGetDatum(tableOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation: %u", tableOid); ++ ++ tblsid.relid = RelationRelationId; ++ tblsid.secid = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ ++ return sepgsql_client_create_secid(tblsid, tclass, relOid); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_database_secid(Oid templateOid) ++{ ++ return get_default_secid_with_database(DatabaseRelationId, ++ templateOid, ++ SEPG_CLASS_DB_DATABASE); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_schema_secid(Oid databaseOid) ++{ ++ return get_default_secid_with_database(NamespaceRelationId, ++ databaseOid, ++ SEPG_CLASS_DB_SCHEMA); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_table_secid(Oid namespaceOid) ++{ ++ return get_default_secid_with_schema(RelationRelationId, ++ namespaceOid, ++ SEPG_CLASS_DB_TABLE); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_sequence_secid(Oid namespaceOid) ++{ ++ return get_default_secid_with_schema(RelationRelationId, ++ namespaceOid, ++ SEPG_CLASS_DB_SEQUENCE); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_view_secid(Oid namespaceOid) ++{ ++ return get_default_secid_with_schema(RelationRelationId, ++ namespaceOid, ++ SEPG_CLASS_DB_VIEW); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_proc_secid(Oid namespaceOid) ++{ ++ return get_default_secid_with_schema(ProcedureRelationId, ++ namespaceOid, ++ SEPG_CLASS_DB_PROCEDURE); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_column_secid(Oid tableOid) ++{ ++ return get_default_secid_with_table(AttributeRelationId, ++ tableOid, ++ SEPG_CLASS_DB_COLUMN); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_tuple_secid(Oid tableOid) ++{ ++ return get_default_secid_with_table(tableOid, ++ tableOid, ++ SEPG_CLASS_DB_TUPLE); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_blob_secid(Oid databaseOid) ++{ ++ return get_default_secid_with_database(LargeObjectMetadataRelationId, ++ databaseOid, ++ SEPG_CLASS_DB_BLOB); ++} ++ ++Oid ++sepgsql_get_default_secid(Relation rel, HeapTuple tuple) ++{ ++ Oid namespaceId; ++ sepgsql_sid_t nsid ++ = { .relid = RelationGetRelid(rel), .secid = InvalidOid }; ++ ++ switch (RelationGetRelid(rel)) ++ { ++ case DatabaseRelationId: ++ case RelationRelationId: ++ case AttributeRelationId: ++ elog(WARNING, "Bug? a new tuple without security id on \"%s\"", ++ RelationGetRelationName(rel)); ++ break; ++ ++ case NamespaceRelationId: ++ nsid = sepgsql_get_default_schema_secid(MyDatabaseId); ++ break; ++ ++ case ProcedureRelationId: ++ namespaceId = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; ++ nsid = sepgsql_get_default_proc_secid(namespaceId); ++ break; ++ ++ case LargeObjectMetadataRelationId: ++ nsid = sepgsql_get_default_blob_secid(MyDatabaseId); ++ break; ++ ++ default: ++ nsid = sepgsql_get_default_tuple_secid(RelationGetRelid(rel)); ++ break; ++ } ++ return nsid.secid; ++} ++ ++/* ++ * a workaround implementation until libselinux/refpolicy don't ++ * support db_schema or other object classes. ++ */ ++static struct { ++ uint16 tclass; ++ char *pattern; ++ char *context; ++} initial_label_catalog[] = { ++ {SEPG_CLASS_DB_DATABASE, "*", ++ "system_u:object_r:sepgsql_db_t:s0"}, ++ {SEPG_CLASS_DB_SCHEMA, "*.*", ++ "system_u:object_r:sepgsql_db_t:s0"}, ++ {SEPG_CLASS_DB_TABLE, "*.pg_catalog.*", ++ "system_u:object_r:sepgsql_sysobj_t:s0"}, ++ {SEPG_CLASS_DB_TABLE, "*.*.*", ++ "system_u:object_r:sepgsql_table_t:s0"}, ++ {SEPG_CLASS_DB_VIEW, "*.*.*", ++ "system_u:object_r:sepgsql_db_t:s0"}, ++ {SEPG_CLASS_DB_SEQUENCE, "*.*.*", ++ "system_u:object_r:sepgsql_db_t:s0"}, ++ {SEPG_CLASS_DB_PROCEDURE, "*.pg_catalog.*", ++ "system_u:object_r:sepgsql_proc_exec_t:s0"}, ++ {SEPG_CLASS_DB_PROCEDURE, "*.*.*", ++ "system_u:object_r:sepgsql_user_proc_exec_t:s0"}, ++ {SEPG_CLASS_DB_COLUMN, "*.pg_catalog.*.*", ++ "system_u:object_r:sepgsql_sysobj_t:s0"}, ++ {SEPG_CLASS_DB_COLUMN, "*.*.*.*", ++ "system_u:object_r:sepgsql_table_t:s0"}, ++ {SEPG_CLASS_DB_TUPLE, "*.pg_catalog.*", ++ "system_u:object_r:sepgsql_sysobj_t:s0"}, ++ {SEPG_CLASS_DB_TUPLE, "*.*.*", ++ "system_u:object_r:sepgsql_table_t:s0"}, ++ {SEPG_CLASS_DB_BLOB, "*.*", ++ "system_u:object_r:sepgsql_blob_t:s0"}, ++ {0, NULL, NULL}, ++}; ++ ++static char * ++lookup_init_catalog(uint16 tclass, const char *name) ++{ ++ int i; ++ ++ for (i = 0; initial_label_catalog[i].pattern; i++) ++ { ++ if (initial_label_catalog[i].tclass == tclass && ++ fnmatch(initial_label_catalog[i].pattern, name, 0) == 0) ++ return initial_label_catalog[i].context; ++ } ++ elog(ERROR, "no valid initial security context for %s (tclass=%d)", ++ name, tclass); ++ return NULL; /* for compiler quiet */ ++} ++ ++static char * ++lookup_init_tuple_label(Oid relOid, HeapTuple tuple) ++{ ++ Oid relNsp = get_rel_namespace(relOid); ++ char namebuf[NAMEDATALEN * 3 + 10]; ++ ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(relNsp), ++ get_rel_name(relOid)); ++ ++ return lookup_init_catalog(SEPG_CLASS_DB_TABLE, namebuf); ++} ++ ++static char * ++lookup_init_database_label(HeapTuple tuple) ++{ ++ Form_pg_database datForm = (Form_pg_database) GETSTRUCT(tuple); ++ ++ return lookup_init_catalog(SEPG_CLASS_DB_DATABASE, ++ NameStr(datForm->datname)); ++} ++ ++static char * ++lookup_init_schema_label(HeapTuple tuple) ++{ ++ Form_pg_namespace nspForm = (Form_pg_namespace) GETSTRUCT(tuple); ++ char namebuf[NAMEDATALEN * 2 + 10]; ++ ++ snprintf(namebuf, sizeof(namebuf), "%s.%s", ++ get_database_name(MyDatabaseId), ++ NameStr(nspForm->nspname)); ++ ++ return lookup_init_catalog(SEPG_CLASS_DB_SCHEMA, namebuf); ++} ++ ++static char * ++lookup_init_relation_label(HeapTuple tuple) ++{ ++ Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); ++ const char *relName = NameStr(classForm->relname); ++ Oid relNsp = classForm->relnamespace; ++ char *seclabel; ++ char namebuf[NAMEDATALEN * 3 + 10]; ++ ++ switch (classForm->relkind) ++ { ++ case RELKIND_RELATION: ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(relNsp), relName); ++ seclabel = lookup_init_catalog(SEPG_CLASS_DB_TABLE, namebuf); ++ break; ++ ++ case RELKIND_SEQUENCE: ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(relNsp), relName); ++ seclabel = lookup_init_catalog(SEPG_CLASS_DB_SEQUENCE, namebuf); ++ break; ++ ++ case RELKIND_VIEW: ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(relNsp), relName); ++ seclabel = lookup_init_catalog(SEPG_CLASS_DB_VIEW, namebuf); ++ break; ++ ++ case RELKIND_INDEX: { ++ HeapTuple tbltup; ++ HeapTuple indtup; ++ Oid tblOid; ++ Oid indOid = HeapTupleGetOid(tuple); ++ ++ indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indOid)); ++ if (!HeapTupleIsValid(indtup)) ++ elog(ERROR, "cache lookup failed for index %u", indOid); ++ ++ tblOid = ((Form_pg_index) GETSTRUCT(indtup))->indrelid; ++ tbltup = SearchSysCache1(RELOID, ObjectIdGetDatum(tblOid)); ++ if (!HeapTupleIsValid(tbltup)) ++ elog(ERROR, "cache lookup failed for relation %u", tblOid); ++ ++ seclabel = lookup_init_relation_label(tbltup); ++ ++ ReleaseSysCache(tbltup); ++ ReleaseSysCache(indtup); ++ ++ break; ++ } ++ case RELKIND_TOASTVALUE: { ++ HeapTuple tbltup; ++ Oid tblOid; ++ ++ /* ++ * XXX - we assume all the toast relation's name is ++ * "pg_toast_%u", and the "%u" shall be replaced by OID ++ * of the relation which owns the toast relation ++ */ ++ tblOid = strtoul(relName + 9, NULL, 10); ++ ++ tbltup = SearchSysCache1(RELOID, ++ ObjectIdGetDatum(tblOid)); ++ if (!HeapTupleIsValid(tbltup)) ++ elog(ERROR, "cache lookup failed for relation %u", tblOid); ++ ++ seclabel = lookup_init_relation_label(tbltup); ++ ++ ReleaseSysCache(tbltup); ++ ++ break; ++ } ++ case RELKIND_COMPOSITE_TYPE: { ++ Oid typOid = classForm->reltype; ++ HeapTuple typtup; ++ ++ typtup = SearchSysCache1(TYPEOID, ++ ObjectIdGetDatum(typOid)); ++ if (!HeapTupleIsValid(typtup)) ++ elog(ERROR, "cache lookup failed for type %u", typOid); ++ ++ seclabel = lookup_init_tuple_label(TypeRelationId, typtup); ++ ++ ReleaseSysCache(typtup); ++ ++ break; ++ } ++ default: ++ elog(ERROR, "unexpected relkind %c of \"%s\"", ++ classForm->relkind, relName); ++ seclabel = NULL; /* compiler quiet */ ++ break; ++ } ++ return seclabel; ++} ++ ++static char * ++lookup_init_attribute_label(HeapTuple tuple) ++{ ++ Form_pg_attribute attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ Oid tblOid = attForm->attrelid; ++ char *seclabel; ++ char namebuf[NAMEDATALEN * 4 + 10]; ++ ++ if (get_rel_relkind(tblOid) == RELKIND_RELATION) ++ { ++ Oid tblNsp = get_rel_namespace(tblOid); ++ ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(tblNsp), ++ get_rel_name(tblOid), ++ NameStr(attForm->attname)); ++ ++ seclabel = lookup_init_catalog(SEPG_CLASS_DB_COLUMN, namebuf); ++ } ++ else ++ { ++ HeapTuple tbltup; ++ ++ tbltup = SearchSysCache1(RELOID, ObjectIdGetDatum(tblOid)); ++ if (!HeapTupleIsValid(tbltup)) ++ elog(ERROR, "cache lookup failed for relation %u", tblOid); ++ ++ seclabel = lookup_init_relation_label(tbltup); ++ ++ ReleaseSysCache(tbltup); ++ } ++ return seclabel; ++} ++ ++static char * ++lookup_init_procedure_label(HeapTuple tuple) ++{ ++ Form_pg_proc proForm = (Form_pg_proc) GETSTRUCT(tuple); ++ Oid proNsp = proForm->pronamespace; ++ char namebuf[NAMEDATALEN * 3 + 10]; ++ ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(proNsp), ++ NameStr(proForm->proname)); ++ ++ return lookup_init_catalog(SEPG_CLASS_DB_PROCEDURE, namebuf); ++} ++ ++static char * ++lookup_init_largeobject_label(HeapTuple tuple) ++{ ++ char namebuf[NAMEDATALEN + 20]; ++ ++ snprintf(namebuf, sizeof(namebuf), "%s.%u", ++ get_database_name(MyDatabaseId), ++ HeapTupleGetOid(tuple)); ++ ++ return lookup_init_catalog(SEPG_CLASS_DB_BLOB, namebuf); ++} ++ ++void ++sepgsql_initial_labeling(void) ++{ ++ Relation classRel; ++ SysScanDesc classScan; ++ ScanKeyData classSkey; ++ HeapTuple classTup; ++ Relation rel; ++ HeapScanDesc scan; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ ++ Assert(IsBootstrapProcessingMode()); ++ ++ StartTransactionCommand(); ++ ++ classRel = heap_open(RelationRelationId, AccessShareLock); ++ ++ ScanKeyInit(&classSkey, ++ Anum_pg_class_relhassecids, ++ BTEqualStrategyNumber, F_BOOLEQ, ++ BoolGetDatum(true)); ++ ++ classScan = systable_beginscan(classRel, InvalidOid, false, ++ SnapshotNow, 1, &classSkey); ++ ++ while (HeapTupleIsValid(classTup = systable_getnext(classScan))) ++ { ++ Oid relOid = HeapTupleGetOid(classTup); ++ ++ Assert(((Form_pg_class) GETSTRUCT(classTup))->relhassecids); ++ ++ rel = heap_open(relOid, RowExclusiveLock); ++ ++ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); ++ ++ while (HeapTupleIsValid(oldtup = heap_getnext(scan, ForwardScanDirection))) ++ { ++ char *label; ++ Oid secid; ++ ++ switch (relOid) ++ { ++ case DatabaseRelationId: ++ label = lookup_init_database_label(oldtup); ++ break; ++ ++ case NamespaceRelationId: ++ label = lookup_init_schema_label(oldtup); ++ break; ++ ++ case RelationRelationId: ++ label = lookup_init_relation_label(oldtup); ++ break; ++ ++ case AttributeRelationId: ++ label = lookup_init_attribute_label(oldtup); ++ break; ++ ++ case ProcedureRelationId: ++ label = lookup_init_procedure_label(oldtup); ++ break; ++ ++ case LargeObjectMetadataRelationId: ++ label = lookup_init_largeobject_label(oldtup); ++ break; ++ ++ default: ++ label = lookup_init_tuple_label(relOid, oldtup); ++ break; ++ } ++ /* ++ * inplace-updating ++ */ ++ newtup = heap_copytuple(oldtup); ++ ++ secid = seclabelTransInput(relOid, label); ++ ++ HeapTupleSetSecid(newtup, secid); ++ ++ heap_inplace_update(rel, newtup); ++ ++ heap_freetuple(newtup); ++ } ++ heap_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++ } ++ systable_endscan(classScan); ++ ++ heap_close(classRel, AccessShareLock); ++ ++ CommitTransactionCommand(); ++} ++ ++char * ++sepgsql_mcstrans_in(char *trans_label) ++{ ++ security_context_t raw_label; ++ security_context_t result; ++ ++ if (!sepgsql_mcstrans) ++ return trans_label; ++ ++ if (selinux_trans_to_raw_context(trans_label, &raw_label) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux: unable to translate \"%s\"", trans_label))); ++ PG_TRY(); ++ { ++ result = pstrdup(raw_label); ++ } ++ PG_CATCH(); ++ { ++ freecon(raw_label); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(raw_label); ++ ++ return result; ++} ++ ++char * ++sepgsql_mcstrans_out(char *raw_label) ++{ ++ security_context_t trans_label; ++ security_context_t result; ++ ++ if (!sepgsql_mcstrans) ++ return raw_label; ++ ++ if (selinux_raw_to_trans_context(raw_label, &trans_label) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux: unable to translate \"%s\"", raw_label))); ++ PG_TRY(); ++ { ++ result = pstrdup(trans_label); ++ } ++ PG_CATCH(); ++ { ++ freecon(trans_label); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(trans_label); ++ ++ return result; ++} ++ ++char * ++sepgsql_rawlabel_in(char *label) ++{ ++ if (!label || security_check_context_raw(label) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_NAME), ++ errmsg("invalid security context \"%s\"", label))); ++ return label; ++} ++ ++char * ++sepgsql_rawlabel_out(char *label) ++{ ++ if (!label || security_check_context_raw(label) < 0) ++ { ++ security_context_t unlabeled_label; ++ ++ if (security_get_initial_context_raw("unlabeled", ++ &unlabeled_label) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("unable to get unlabeled security context"))); ++ PG_TRY(); ++ { ++ label = pstrdup(unlabeled_label); ++ } ++ PG_CATCH(); ++ { ++ freecon(unlabeled_label); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(unlabeled_label); ++ } ++ return label; ++} +diff --git a/src/backend/sepgsql/misc.c b/src/backend/sepgsql/misc.c +new file mode 100644 +index 0000000..4f29cd7 +--- /dev/null ++++ b/src/backend/sepgsql/misc.c +@@ -0,0 +1,123 @@ ++/* ++ * misc.c ++ * ++ * SELinux hooks related to misc features ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "libpq/libpq-be.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/guc.h" ++ ++#include ++ ++/* ++ * sepgsql_client_label ++ * ++ * security context of the peer process ++ */ ++static char *sepgsql_client_label = NULL; ++ ++char * ++sepgsql_get_client_label(void) ++{ ++ return sepgsql_client_label; ++} ++ ++char * ++sepgsql_set_client_label(char *new_label) ++{ ++ char *old_label = sepgsql_client_label; ++ ++ sepgsql_client_label = new_label; ++ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_avc_switch(new_label); ++ } ++#endif ++ return old_label; ++} ++ ++void ++sepgsql_post_bootstraping(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ sepgsql_initial_labeling(); ++#endif ++} ++ ++void ++sepgsql_initialize(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ security_context_t context; ++ ++ /* init userspace avc */ ++ sepgsql_avc_init(); ++ ++ /* init privilege of the client */ ++ if (!MyProcPort) ++ { ++ /* ++ * SE-PgSQL does not prevent anything in single-user mode. ++ */ ++ sepostgresql_mode = SEPGSQL_MODE_INTERNAL; ++ ++ if (getprevcon_raw(&context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux: could not get server context"))); ++ } ++ else ++ { ++ if (getpeercon_raw(MyProcPort->sock, &context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux: could not get client context"))); ++ } ++ sepgsql_set_client_label(context); ++ ++ return; ++ } ++#endif ++ if (default_with_secids) ++ { ++ default_with_secids = false; ++ elog(LOG, "guc: default_with_secid was turned off " ++ "because no label based access control is availabel now"); ++ } ++} ++ ++bool ++sepgsql_worker_needed(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ return true; ++#endif ++ return false; ++} ++ ++void ++sepgsql_worker_main(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_avc_worker_main(); ++ return; ++ } ++#endif ++ elog(FATAL, "Bug? try to launch worker process without security provider"); ++} ++ +diff --git a/src/backend/sepgsql/operator.c b/src/backend/sepgsql/operator.c +new file mode 100644 +index 0000000..7960525 +--- /dev/null ++++ b/src/backend/sepgsql/operator.c +@@ -0,0 +1,454 @@ ++/* ++ * operator.c ++ * ++ * SELinux hooks related to operators ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_operator.h" ++#include "catalog/pg_opclass.h" ++#include "catalog/pg_opfamily.h" ++#include "catalog/pg_seclabel.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/builtins.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_operator_common(Oid operOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ char *auname; ++ bool retval; ++ ++ tsid.relid = OperatorRelationId; ++ tsid.secid = GetSysCacheSecid1(OPEROID, ObjectIdGetDatum(operOid)); ++ ++ auname = format_operator(operOid); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ pfree(auname); ++ ++ return retval; ++} ++ ++bool ++sepgsql_opclass_common(Oid opcOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for operator class %u", opcOid); ++ ++ tsid.relid = OperatorClassRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++bool ++sepgsql_opfamily_common(Oid opfOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for operator family %u", opfOid); ++ ++ tsid.relid = OperatorFamilyRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_operator_namespace(Oid operOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId; ++ ++ tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_operator) GETSTRUCT(tuple))->oprnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++static Oid ++get_opclass_namespace(Oid opcOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++static Oid ++get_opfamily_namespace(Oid opfOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++#endif ++ ++Oid ++sepgsql_operator_create(const char *operName, Oid replaced, Oid namespaceId, ++ Oid codeFunc, Oid restrictFunc, Oid joinFunc, ++ Oid commutatorOp, Oid negatorOp) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ uint32 required; ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_tuple:{insert} */ ++ if (OidIsValid(replaced)) ++ { ++ nsid.relid = OperatorRelationId; ++ nsid.secid = GetSysCacheSecid1(OPEROID, ++ ObjectIdGetDatum(replaced)); ++ required = SEPG_DB_TUPLE__UPDATE; ++ } ++ else ++ { ++ nsid = sepgsql_get_default_tuple_secid(OperatorRelationId); ++ required = SEPG_DB_TUPLE__INSERT; ++ } ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ operName, ++ true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(codeFunc)) ++ sepgsql_proc_common(codeFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(restrictFunc)) ++ sepgsql_proc_common(restrictFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(joinFunc)) ++ sepgsql_proc_common(joinFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* ++ * XXX - we should check anything on he commutatorOp/negatorOp ++ */ ++ ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_operator_alter(Oid operOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_operator_common(operOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_operator_relabel(Oid operOid, char *newLabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ nsid.relid = OperatorRelationId; ++ nsid.secid = seclabelTransInput(OperatorRelationId, newLabel); ++ ++ /* db_tuple:{update relabelfrom} */ ++ sepgsql_operator_common(operOid, ++ SEPG_DB_TUPLE__UPDATE | ++ SEPG_DB_TUPLE__RELABELFROM, ++ true); ++ ++ /* db_tuple:{relabelto} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELTO, ++ format_operator(operOid), ++ true); ++ return nsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_operator_drop(Oid operOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_operator_namespace(operOid); ++ ++ /* db_namespace:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_operator_common(operOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_operator_comment(Oid operOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_operator_common(operOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_opclass_create(const char *opcName, Oid namespaceId, ++ Oid typeOid, Oid opfamilyOid, Oid storageOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_TUPLE__UPDATE, true); ++ ++ /* db_tuple:{insert} */ ++ nsid = sepgsql_get_default_tuple_secid(OperatorRelationId); ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ opcName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_opclass_alter(Oid opcOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_opclass_alter_rename(Oid opcOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_opclass_namespace(opcOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_opclass_drop(Oid opcOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_opclass_namespace(opcOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{drop} */ ++ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_opclass_comment(Oid opcOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_opfamily_create(const char *opfName, Oid namespaceId, Oid amOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_tuple:{insert} */ ++ nsid = sepgsql_get_default_tuple_secid(OperatorFamilyRelationId); ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ opfName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_opfamily_alter(Oid opfOid, bool isDrop, Oid amOid, ++ List *operators, List *procedures) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); ++ ++ /* XXX - to do we should install checks? */ ++ } ++#endif ++} ++ ++void ++sepgsql_opfamily_alter_rename(Oid opfOid, const char *newName) ++{ ++ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_opfamily_namespace(opfOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_opfamily_alter_owner(Oid opfOid, Oid newOwner) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_opfamily_drop(Oid opfOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_opfamily_namespace(opfOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_opfamily_comment(Oid opfOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/proc.c b/src/backend/sepgsql/proc.c +new file mode 100644 +index 0000000..1149571 +--- /dev/null ++++ b/src/backend/sepgsql/proc.c +@@ -0,0 +1,366 @@ ++/* ++ * proc.c ++ * ++ * SELinux hooks related to procedures ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_aggregate.h" ++#include "catalog/pg_language.h" ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_seclabel.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/builtins.h" ++#include "utils/syscache.h" ++#include "utils/lsyscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_proc_common(Oid procOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ char *auname; ++ bool retval; ++ ++ tsid.relid = ProcedureRelationId; ++ tsid.secid = GetSysCacheSecid1(PROCOID, ++ ObjectIdGetDatum(procOid)); ++ ++ auname = format_procedure(procOid); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_PROCEDURE, ++ required, ++ auname, ++ abort); ++ pfree(auname); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_proc_create(const char *proName, Oid replaced, ++ Oid namespaceId, Oid languageId) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ HeapTuple tuple; ++ sepgsql_sid_t nsid; ++ char *scontext; ++ char *tcontext; ++ uint32 required; ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{create or setattr} */ ++ if (OidIsValid(replaced)) ++ { ++ nsid.relid = ProcedureRelationId; ++ nsid.secid = GetSysCacheSecid1(PROCOID, ++ ObjectIdGetDatum(replaced)); ++ required = SEPG_DB_PROCEDURE__SETATTR; ++ } ++ else ++ { ++ nsid = sepgsql_get_default_proc_secid(namespaceId); ++ required = SEPG_DB_PROCEDURE__CREATE; ++ } ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_PROCEDURE, ++ required, ++ proName, ++ true); ++ ++ /* db_language:{implemente} */ ++ tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(languageId)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for language %u", languageId); ++ ++ scontext = seclabelRawOutput(LanguageRelationId, ++ HeapTupleGetSecid(tuple)); ++ tcontext = seclabelRawOutput(nsid.relid, nsid.secid); ++ ++ sepgsql_compute_perms(scontext, tcontext, ++ SEPG_CLASS_DB_LANGUAGE, ++ SEPG_DB_LANGUAGE__IMPLEMENTE, ++ proName, true); ++ ++ ReleaseSysCache(tuple); ++ ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_proc_alter(Oid procOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_proc_alter_rename(Oid procOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_func_namespace(procOid); ++ ++ /* db_schema:{remove_name add_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__REMOVE_NAME | ++ SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{setattr} */ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_proc_alter_schema(Oid procOid, Oid newSchema) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_func_namespace(procOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(newSchema, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{setattr} */ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_proc_relabel(Oid procOid, char *new_label) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ ++ tsid.relid = ProcedureRelationId; ++ tsid.secid = seclabelTransInput(tsid.relid, new_label); ++ ++ /* db_procedure:{setattr relabelfrom} */ ++ sepgsql_proc_common(procOid, ++ SEPG_DB_PROCEDURE__SETATTR | ++ SEPG_DB_PROCEDURE__RELABELFROM, true); ++ ++ /* db_procedure:{relabelto} */ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_DB_PROCEDURE__RELABELTO, ++ format_procedure(procOid), ++ true); ++ return tsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_proc_drop(Oid procOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_func_namespace(procOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_procedure:{proc} */ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__DROP, true); ++ } ++#endif ++} ++ ++void ++sepgsql_proc_grant(Oid procOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_proc_comment(Oid procOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_proc_execute(Oid procOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__EXECUTE, true); ++ } ++#endif ++} ++ ++bool ++sepgsql_proc_be_inlined(HeapTuple protup) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ char *old_label; ++ char *new_label; ++ const char *auname ++ = NameStr(((Form_pg_proc) GETSTRUCT(protup))->proname); ++ ++ tsid.relid = ProcedureRelationId; ++ tsid.secid = HeapTupleGetSecid(protup); ++ ++ if (!sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_DB_PROCEDURE__EXECUTE, ++ auname, false)) ++ return false; ++ ++ old_label = sepgsql_get_client_label(); ++ new_label = sepgsql_client_create_label(tsid, SEPG_CLASS_PROCESS); ++ if (strcmp(old_label, new_label) != 0) ++ return false; ++ ++ return true; ++ } ++#endif ++ return true; ++} ++ ++char * ++sepgsql_proc_domtrans(HeapTuple protup, MemoryContext mcxt) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ char *old_label = sepgsql_get_client_label(); ++ char *new_label; ++ char *auname ++ = NameStr(((Form_pg_proc) GETSTRUCT(protup))->proname); ++ ++ tsid.relid = ProcedureRelationId; ++ tsid.secid = HeapTupleGetSecid(protup); ++ ++ new_label = sepgsql_client_create_label(tsid, SEPG_CLASS_PROCESS); ++ ++ if (strcmp(old_label, new_label) == 0) ++ return NULL; ++ ++ /* db_procedure:{entrypoint} */ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_DB_PROCEDURE__ENTRYPOINT, ++ auname, ++ true); ++ ++ /* db_process:{transition} */ ++ sepgsql_compute_perms(old_label, ++ new_label, ++ SEPG_CLASS_PROCESS, ++ SEPG_PROCESS__TRANSITION, ++ NULL, ++ true); ++ ++ return MemoryContextStrdup(mcxt, new_label); ++ } ++#endif ++ return NULL; ++} ++ ++Oid ++sepgsql_aggregate_create(const char *aggName, Oid namespaceId, ++ Oid transFunc, Oid finalFunc) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid securityId; ++ ++ /* check normal creation permission */ ++ securityId = sepgsql_proc_create(aggName, InvalidOid, ++ namespaceId, INTERNALlanguageId); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(transFunc)) ++ sepgsql_proc_common(transFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(finalFunc)) ++ sepgsql_proc_common(finalFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return securityId; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_aggregate_execute(Oid aggOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Form_pg_aggregate aggForm; ++ HeapTuple tuple; ++ ++ tuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for aggregate %u", aggOid); ++ ++ aggForm = (Form_pg_aggregate) GETSTRUCT(tuple); ++ ++ /* pg_proc:{execute} */ ++ sepgsql_proc_common(aggOid, SEPG_DB_PROCEDURE__EXECUTE, true); ++ ++ /* pg_proc:{execute} */ ++ if (OidIsValid(aggForm->aggtransfn)) ++ sepgsql_proc_common(aggForm->aggtransfn, ++ SEPG_DB_PROCEDURE__EXECUTE, true); ++ ++ /* pg_proc:{execute} */ ++ if (OidIsValid(aggForm->aggfinalfn)) ++ sepgsql_proc_common(aggForm->aggfinalfn, ++ SEPG_DB_PROCEDURE__EXECUTE, true); ++ ++ ReleaseSysCache(tuple); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/proxy.c b/src/backend/sepgsql/proxy.c +new file mode 100644 +index 0000000..32bb57a +--- /dev/null ++++ b/src/backend/sepgsql/proxy.c +@@ -0,0 +1,129 @@ ++/* ++ * proxy.c ++ * ++ * mandatory query rewriting support ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/namespace.h" ++#include "lib/stringinfo.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/builtins.h" ++#include "utils/lsyscache.h" ++#include "tcop/tcopprot.h" ++ ++#ifdef HAVE_SELINUX ++/* ++ * If row-level access control is configured, COPY TO statement ++ * shall be rewritten to SELECT * statement. ++ */ ++static void ++sepgsql_proxy_copy_stmt(CopyStmt *stmt) ++{ ++ Oid relOid; ++ Oid namespaceId; ++ List *queries; ++ ListCell *l, *p = NULL; ++ bool with_oids = false; ++ StringInfoData qbuf; ++ ++ /* no need to do nothing */ ++ if (stmt->is_from || !stmt->relation) ++ return; ++ ++ /* obtain relaion ID */ ++ relOid = RangeVarGetRelid(stmt->relation, false); ++ ++ namespaceId = get_rel_namespace(relOid); ++ ++ /* Is there WITH OID option? */ ++retry: ++ foreach (l, stmt->options) ++ { ++ DefElem *defel = (DefElem *) lfirst(l); ++ ++ Assert(IsA(defel, DefElem)); ++ if (strcmp(defel->defname, "oids") == 0) ++ { ++ with_oids = true; ++ stmt->options = list_delete_cell(stmt->options, l, p); ++ goto retry; ++ } ++ p = l; ++ } ++ ++ /* Make a query */ ++ initStringInfo(&qbuf); ++ ++ appendStringInfo(&qbuf, "SELECT %s", with_oids ? "oid" : ""); ++ ++ if (stmt->attlist == NIL) ++ appendStringInfo(&qbuf, "%s*", with_oids ? "," : ""); ++ else ++ { ++ bool need_comma = with_oids; ++ ++ foreach (l, stmt->attlist) ++ { ++ appendStringInfo(&qbuf, "%s%s", ++ need_comma ? "," : "", ++ strVal(lfirst(l))); ++ need_comma = true; ++ } ++ } ++ ++ appendStringInfo(&qbuf, " FROM ONLY %s.%s", ++ quote_identifier(get_namespace_name(namespaceId)), ++ quote_identifier(get_rel_name(relOid))); ++ ++ queries = pg_parse_query(qbuf.data); ++ ++ Assert(list_length(queries) == 1); ++ ++ /* update CopyStmt */ ++ stmt->query = lfirst(list_head(queries)); ++ stmt->relation = NULL; ++} ++#endif ++ ++void ++sepgsql_proxy_queries(List *queryList) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ ListCell *l; ++ ++ foreach (l, queryList) ++ { ++ Query *qry = lfirst(l); ++ ++ switch (qry->commandType) ++ { ++ case CMD_SELECT: ++ break; ++ ++ case CMD_UPDATE: ++ case CMD_INSERT: ++ case CMD_DELETE: ++ /* we have no rewrite policy */ ++ break; ++ ++ case CMD_UTILITY: ++ Assert(qry->utilityStmt != NULL); ++ if (IsA(qry->utilityStmt, CopyStmt)) ++ sepgsql_proxy_copy_stmt((CopyStmt *)qry->utilityStmt); ++ break; ++ ++ default: ++ /* CMD_UNKNOWN or CMD_NOTHING */ ++ break; ++ } ++ } ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/relation.c b/src/backend/sepgsql/relation.c +new file mode 100644 +index 0000000..2604749 +--- /dev/null ++++ b/src/backend/sepgsql/relation.c +@@ -0,0 +1,819 @@ ++/* ++ * relation.c ++ * ++ * SELinux hooks related to relation ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/sysattr.h" ++#include "catalog/catalog.h" ++#include "catalog/heap.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_class.h" ++#include "catalog/pg_seclabel.h" ++#include "catalog/pg_type.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_relation_common(Oid relOid, uint32 required, bool abort) ++{ ++ Form_pg_class classForm; ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ uint16 tclass; ++ bool retval; ++ ++ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relOid); ++ classForm = (Form_pg_class) GETSTRUCT(tuple); ++ ++ tsid.relid = RelationRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ switch (classForm->relkind) ++ { ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ break; ++ ++ case RELKIND_COMPOSITE_TYPE: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ ++ default: ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ } ++ ++ retval = sepgsql_client_perms(tsid, ++ tclass, ++ required, ++ NameStr(classForm->relname), ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Bitmapset * ++fixup_whole_row_reference(Oid relOid, int natts, Bitmapset *columns) ++{ ++ Bitmapset *result; ++ AttrNumber attno; ++ ++ attno = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber; ++ if (!bms_is_member(attno, columns)) ++ return columns; /* no need to fixup */ ++ ++ result = bms_copy(columns); ++ result = bms_del_member(result, attno); ++ ++ for (attno=1; attno <= natts; attno++) ++ { ++ Form_pg_attribute attForm; ++ HeapTuple atttup; ++ ++ atttup = SearchSysCache2(ATTNUM, ++ ObjectIdGetDatum(relOid), ++ Int16GetDatum(attno)); ++ if (!HeapTupleIsValid(atttup)) ++ continue; ++ ++ attForm = (Form_pg_attribute) GETSTRUCT(atttup); ++ if (!attForm->attisdropped) ++ { ++ int cindex = attno - FirstLowInvalidHeapAttributeNumber; ++ result = bms_add_member(result, cindex); ++ } ++ ReleaseSysCache(atttup); ++ } ++ ++ return result; ++} ++#endif ++ ++bool ++sepgsql_relation_perms(Oid relOid, AclMode aclmask, ++ Bitmapset *selectedCols, ++ Bitmapset *modifiedCols, bool abort) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Form_pg_class relForm; ++ HeapTuple tuple; ++ sepgsql_sid_t rsid; ++ Bitmapset *selColsEx; ++ Bitmapset *modColsEx; ++ Bitmapset *columns; ++ AttrNumber nattrs; ++ AttrNumber attno; ++ const char *auname; ++ char relkind; ++ uint16 tclass = 0; ++ uint32 required = 0; ++ bool rc = true; ++ ++ /* ++ * Hardwired policy: ++ * SE-PostgreSQL enforces clients cannot modify system catalogs ++ * and access toast values using DML statements in enforcing mode. ++ * Note that it performs in permissive mode during initdb phase. ++ */ ++ if (sepgsql_get_enforce()) ++ { ++ if (IsSystemNamespace(get_rel_namespace(relOid)) && ++ (aclmask & (ACL_UPDATE | ACL_INSERT | ACL_DELETE)) != 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("not allowed to modify system catalog \"%s\"", ++ get_rel_name(relOid)))); ++ ++ if (get_rel_relkind(relOid) == RELKIND_TOASTVALUE) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("not allowed to access toast values \"%s\"", ++ get_rel_name(relOid)))); ++ } ++ ++ /* ++ * check relation's permissions ++ */ ++ tuple = SearchSysCache1(RELOID, ++ ObjectIdGetDatum(relOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relOid); ++ ++ rsid.relid = RelationRelationId; ++ rsid.secid = HeapTupleGetSecid(tuple); ++ ++ relForm = (Form_pg_class) GETSTRUCT(tuple); ++ auname = NameStr(relForm->relname); ++ relkind = relForm->relkind; ++ nattrs = relForm->relnatts; ++ ++ switch (relkind) ++ { ++ case RELKIND_RELATION: ++ tclass = SEPG_CLASS_DB_TABLE; ++ ++ if (aclmask & ACL_SELECT) ++ required |= SEPG_DB_TABLE__SELECT; ++ if (aclmask & ACL_INSERT) ++ required |= SEPG_DB_TABLE__INSERT; ++ if (aclmask & ACL_UPDATE) ++ required |= (!modifiedCols ++ ? SEPG_DB_TABLE__LOCK ++ : SEPG_DB_TABLE__UPDATE); ++ if (aclmask & ACL_DELETE) ++ required |= SEPG_DB_TABLE__DELETE; ++ break; ++ ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ if (aclmask & ACL_SELECT) ++ required |= SEPG_DB_SEQUENCE__GET_VALUE; ++ break; ++ ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ if (aclmask != 0) ++ required |= SEPG_DB_VIEW__EXPAND; ++ break; ++ ++ default: ++ elog(ERROR, "Bug? unexpected relkind %c", relkind); ++ return false; ++ } ++ ++ if (required != 0) ++ rc = sepgsql_client_perms(rsid, tclass, required, auname, abort); ++ ++ ReleaseSysCache(tuple); ++ ++ if (!rc || relkind != RELKIND_RELATION) ++ return rc; ++ ++ /* ++ * Check column's permissions ++ */ ++ selColsEx = fixup_whole_row_reference(relOid, nattrs, ++ selectedCols); ++ modColsEx = fixup_whole_row_reference(relOid, nattrs, ++ modifiedCols); ++ columns = bms_union(selColsEx, modColsEx); ++ ++ while ((attno = bms_first_member(columns)) >= 0) ++ { ++ required = 0; ++ ++ if (bms_is_member(attno, selColsEx)) ++ required |= SEPG_DB_COLUMN__SELECT; ++ if (bms_is_member(attno, modColsEx)) ++ { ++ if (aclmask & ACL_UPDATE) ++ required |= SEPG_DB_COLUMN__UPDATE; ++ if (aclmask & ACL_INSERT) ++ required |= SEPG_DB_COLUMN__INSERT; ++ } ++ if (required == 0) ++ continue; ++ ++ attno += FirstLowInvalidHeapAttributeNumber; ++ rc = sepgsql_attribute_common(relOid, attno, required, abort); ++ if (!rc) ++ break; ++ } ++ ++ if (selColsEx != selectedCols) ++ bms_free(selColsEx); ++ if (modColsEx != modifiedCols) ++ bms_free(modColsEx); ++ bms_free(columns); ++ ++ return rc; ++ } ++#endif ++ return true; ++} ++ ++Oid * ++sepgsql_relation_create(const char *relName, ++ char relkind, ++ TupleDesc tupDesc, ++ Oid namespaceId, ++ List *supOids, ++ bool createAs) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ sepgsql_sid_t csid; ++ Oid *secLabels; ++ uint16 tclass; ++ uint32 perms; ++ AttrNumber index, attno, nitems; ++ ++ /* ++ * The secLabeld array stores security identifiers to be ++ * assigned on the new table and columns. ++ * ++ * secLabels[0] is security-id of the relation. ++ * secLabels[attnum - FirstLowInvalidHeapAttributeNumber] ++ * is security-id of the columns. ++ */ ++ secLabels = seclabelMakeRelationDefaults(tupDesc, supOids); ++ nitems = tupDesc->natts - FirstLowInvalidHeapAttributeNumber; ++ ++ switch (relkind) ++ { ++ case RELKIND_RELATION: ++ if (!OidIsValid(secLabels[0])) ++ { ++ tsid = sepgsql_get_default_table_secid(namespaceId); ++ secLabels[0] = tsid.secid; ++ } ++ tclass = SEPG_CLASS_DB_TABLE; ++ perms = SEPG_DB_TABLE__CREATE; ++ if (createAs) ++ perms |= SEPG_DB_TABLE__INSERT; ++ break; ++ ++ case RELKIND_SEQUENCE: ++ if (!OidIsValid(secLabels[0])) ++ { ++ tsid = sepgsql_get_default_sequence_secid(namespaceId); ++ secLabels[0] = tsid.secid; ++ } ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ perms = SEPG_DB_SEQUENCE__CREATE; ++ break; ++ ++ case RELKIND_VIEW: ++ if (!OidIsValid(secLabels[0])) ++ { ++ tsid = sepgsql_get_default_view_secid(namespaceId); ++ secLabels[0] = tsid.secid; ++ } ++ tclass = SEPG_CLASS_DB_VIEW; ++ perms = SEPG_DB_VIEW__CREATE; ++ break; ++ ++ case RELKIND_COMPOSITE_TYPE: ++ if (!OidIsValid(secLabels[0])) ++ { ++ tsid = sepgsql_get_default_tuple_secid(TypeRelationId); ++ secLabels[0] = seclabelMoveSecid(RelationRelationId, ++ TypeRelationId, tsid.secid); ++ } ++ tclass = SEPG_CLASS_DB_TUPLE; ++ perms = SEPG_DB_TUPLE__INSERT; ++ break; ++ ++ default: ++ elog(ERROR, "Bug? unexpected relkind %c", relkind); ++ return NULL; ++ } ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_table:{create} or others */ ++ tsid.relid = RelationRelationId; ++ tsid.secid = secLabels[0]; ++ sepgsql_client_perms(tsid, tclass, perms, relName, true); ++ ++ /* no individual security-id except for RELKIND_RELATION */ ++ if (relkind != RELKIND_RELATION) ++ { ++ Oid securityId = seclabelMoveSecid(AttributeRelationId, ++ RelationRelationId, secLabels[0]); ++ ++ for (index = 1; index < nitems; index++) ++ secLabels[index] = securityId; ++ ++ return secLabels; ++ } ++ ++ /* ++ * security context of the columns ++ */ ++ for (index = 1; index < nitems; index++) ++ { ++ Form_pg_attribute attForm; ++ char auname[NAMEDATALEN * 2 + 10]; ++ ++ attno = index + FirstLowInvalidHeapAttributeNumber; ++ ++ /* skip unnecessary system columns */ ++ if ((attno == ObjectIdAttributeNumber && !tupDesc->tdhasoid) || ++ (attno == SecurityLabelAttributeNumber && !tupDesc->tdhassecid)) ++ continue; ++ ++ if (!OidIsValid(secLabels[index])) ++ { ++ csid = sepgsql_client_create_secid(tsid, ++ SEPG_CLASS_DB_COLUMN, ++ AttributeRelationId); ++ secLabels[index] = csid.secid; ++ } ++ ++ if (attno < 0) ++ attForm = SystemAttributeDefinition(attno, ++ tupDesc->tdhasoid, ++ tupDesc->tdhassecid); ++ else ++ attForm = tupDesc->attrs[attno]; ++ ++ /* db_column:{create (insert)} permission */ ++ csid.relid = AttributeRelationId; ++ csid.secid = secLabels[index]; ++ ++ perms = SEPG_DB_COLUMN__CREATE; ++ if (createAs && attno >= 0) ++ perms |= SEPG_DB_COLUMN__INSERT; ++ ++ snprintf(auname, sizeof(auname), "%s.%s", ++ relName, NameStr(attForm->attname)); ++ ++ sepgsql_client_perms(csid, ++ SEPG_CLASS_DB_COLUMN, ++ perms, ++ auname, ++ true); ++ } ++ return secLabels; ++ } ++#endif ++ return NULL; ++} ++ ++void ++sepgsql_relation_alter(Oid relationOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_alter_schema(Oid relationOid, Oid newSchema) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_alter_rename(Oid relationOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_alter_inherit(Oid childOid, Oid parentOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(childOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_relation_relabel(Oid relationOid, char *new_label) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char *auname; ++ char relkind; ++ uint16 tclass = 0; ++ ++ nsid.relid = RelationRelationId; ++ nsid.secid = seclabelTransInput(nsid.relid, new_label); ++ ++ auname = get_rel_name(relationOid); ++ relkind = get_rel_relkind(relationOid); ++ ++ switch (relkind) ++ { ++ case RELKIND_RELATION: ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ break; ++ ++ case RELKIND_COMPOSITE_TYPE: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ ++ default: ++ elog(ERROR, "unexpected relkind %c", relkind); ++ break;; ++ } ++ /* db_xxx:{setattr relabelfrom} */ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR | ++ SEPG_DB_TABLE__RELABELFROM, true); ++ ++ /* db_xxx:{relabelto} */ ++ sepgsql_client_perms(nsid, ++ tclass, ++ SEPG_DB_TABLE__RELABELTO, ++ auname, true); ++ pfree(auname); ++ ++ return nsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_relation_drop(Oid relationOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__DROP, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_getattr(Oid relationOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__GETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_grant(Oid relationOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_comment(Oid relationOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++bool ++sepgsql_relation_cluster(Oid relationOid, bool abort) ++{ ++#ifdef HAVE_SELINUX ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ ++ if (sepgsql_is_enabled()) ++ { ++ bool retval = ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__INDEXON, true); ++ return retval; ++ } ++#endif ++ return true; ++} ++ ++void ++sepgsql_relation_truncate(Relation rel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); ++ ++ /* db_table:{delete} */ ++ sepgsql_relation_common(RelationGetRelid(rel), ++ SEPG_DB_TABLE__DELETE, true); ++ /* db_tuple:{delete} */ ++ } ++#endif ++} ++ ++void ++sepgsql_relation_lock(Relation rel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); ++ ++ /* db_table:{lock} */ ++ sepgsql_relation_common(RelationGetRelid(rel), ++ SEPG_DB_TABLE__LOCK, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_reindex(Oid relationOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ ++ /* db_table:{indexon} */ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__INDEXON, true); ++ } ++#endif ++} ++ ++void ++sepgsql_view_replace(Oid viewOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(viewOid) == RELKIND_VIEW); ++ /* db_view:{setattr} */ ++ sepgsql_relation_common(viewOid, ++ SEPG_DB_VIEW__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_index_create(Oid relationOid, Oid namespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__ADD_NAME, true); ++ /* db_table:{setattr indexon} */ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR | ++ SEPG_DB_TABLE__INDEXON, true); ++ } ++#endif ++} ++ ++void ++sepgsql_index_reindex(Oid indexOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ HeapTuple tuple; ++ Oid relationOid; ++ ++ tuple = SearchSysCache1(INDEXRELID, indexOid); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for index %u", indexOid); ++ relationOid = ((Form_pg_index) GETSTRUCT(tuple))->indrelid; ++ ReleaseSysCache(tuple); ++ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__INDEXON, true); ++ } ++#endif ++} ++ ++void ++sepgsql_sequence_get_value(Oid sequenceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE); ++ sepgsql_relation_common(sequenceOid, ++ SEPG_DB_SEQUENCE__GET_VALUE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_sequence_next_value(Oid sequenceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE); ++ sepgsql_relation_common(sequenceOid, ++ SEPG_DB_SEQUENCE__NEXT_VALUE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_sequence_set_value(Oid sequenceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE); ++ sepgsql_relation_common(sequenceOid, ++ SEPG_DB_SEQUENCE__SET_VALUE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_rule_create(Oid relationOid, const char *ruleName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_rule_drop(Oid relationOid, const char *ruleName, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled() && !cascade) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_rule_comment(Oid relationOid, const char *ruleName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_trigger_create(Oid relationOid, const char *triggerName, ++ Oid constrrelid, Oid funcOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ ++ /* db_table:{setattr} */ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ /* db_procedure:{install} */ ++ // sepgsql_procedure_common... ++ } ++#endif ++} ++ ++void ++sepgsql_trigger_alter(Oid relationOid, const char *triggerName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_trigger_drop(Oid relationOid, const char *triggerName, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled() && !cascade) ++ { ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_trigger_comment(Oid relationOid, const char *triggerName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_constraint_comment(Oid relationOid, const char *constName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/role.c b/src/backend/sepgsql/role.c +new file mode 100644 +index 0000000..3c4891c +--- /dev/null ++++ b/src/backend/sepgsql/role.c +@@ -0,0 +1,142 @@ ++/* ++ * role.c ++ * ++ * SELinux hooks related to roles ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_authid.h" ++#include "catalog/pg_seclabel.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_role_common(Oid roleOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for role %u", roleOid); ++ ++ tsid.relid = AuthIdRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_role_create(const char *roleName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ nsid = sepgsql_get_default_tuple_secid(AuthIdRelationId); ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ roleName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_role_alter(Oid roleOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_role_relabel(Oid roleOid, char *newLabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ ++ tsid.relid = AuthIdRelationId; ++ tsid.secid = seclabelTransInput(tsid.relid, newLabel); ++ ++ /* db_tuple:{update relabelfrom} */ ++ sepgsql_role_common(roleOid, ++ SEPG_DB_TUPLE__UPDATE | ++ SEPG_DB_TUPLE__RELABELFROM, ++ true); ++ ++ /* db_tuple:{relabelto} */ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELTO, ++ GetUserNameFromId(roleOid), ++ true); ++ return tsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_role_drop(Oid roleOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_role_grant(Oid roleOid, bool is_grant, List *memberIds) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_role_comment(Oid roleOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/rowlv.c b/src/backend/sepgsql/rowlv.c +new file mode 100644 +index 0000000..9ace5e4 +--- /dev/null ++++ b/src/backend/sepgsql/rowlv.c +@@ -0,0 +1,367 @@ ++/* ++ * rowlv.c ++ * ++ * Row-level access control facilities ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/heapam.h" ++#include "access/sysattr.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_class.h" ++#include "catalog/pg_database.h" ++#include "catalog/pg_language.h" ++#include "catalog/pg_largeobject_metadata.h" ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_seclabel.h" ++#include "catalog/pg_type.h" ++#include "nodes/makefuncs.h" ++#include "parser/parsetree.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "storage/bufmgr.h" ++#include "utils/fmgroids.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++ ++static int sepgsql_rowlv_mode = SEPGSQL_ROWLV_FILTER; ++ ++int ++sepgsql_rowlv_get_mode(void) ++{ ++ return sepgsql_rowlv_mode; ++} ++ ++int ++sepgsql_rowlv_set_mode(int new_mode) ++{ ++ int old_mode = sepgsql_rowlv_mode; ++ ++ Assert(new_mode == SEPGSQL_ROWLV_FILTER || ++ new_mode == SEPGSQL_ROWLV_ABORT || ++ new_mode == SEPGSQL_ROWLV_BYPASS); ++ ++ sepgsql_rowlv_mode = new_mode; ++ ++ return old_mode; ++} ++ ++void ++sepgsql_rowlv_add_policy(PlannerInfo *root, Scan *scan) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled() && scan->scanrelid > 0) ++ { ++ RangeTblEntry *rte = planner_rt_fetch(scan->scanrelid, root); ++ Form_pg_class classForm; ++ HeapTuple tuple; ++ FuncExpr *func; ++ Var *v1; /* tableoid */ ++ Var *v2; /* row reference */ ++ Const *c3; /* required permissions */ ++ Const *c4; /* abort? or filter? */ ++ bool abort; ++ bool relhassecids; ++ Oid reltype; ++ ++ if (sepgsql_rowlv_mode == SEPGSQL_ROWLV_BYPASS) ++ return; ++ ++ Assert(IsA(rte, RangeTblEntry)); ++ if (rte->rowlvPerms == 0) ++ return; ++ ++ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(rte->relid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", rte->relid); ++ ++ classForm = (Form_pg_class) GETSTRUCT(tuple); ++ reltype = classForm->reltype; ++ relhassecids = classForm->relhassecids; ++ ++ ReleaseSysCache(tuple); ++ ++ /* ++ * In the case when tuples have no individual security labels, ++ * sepgsql_relation_perms() checks permissions on the relation's ++ * label, as if it is security label of the whole tuples. ++ */ ++ if (!relhassecids) ++ return; ++ ++ /* 1st argument : tableoid */ ++ v1 = makeVar(scan->scanrelid, ++ TableOidAttributeNumber, ++ OIDOID, ++ -1, ++ 0); ++ ++ /* 2nd argument : whole row reference */ ++ v2 = makeVar(scan->scanrelid, ++ InvalidAttrNumber, ++ reltype, ++ -1, ++ 0); ++ ++ /* 3rd argument : required permissions */ ++ c3 = makeConst(INT4OID, ++ -1, ++ sizeof(int32), ++ Int32GetDatum(rte->rowlvPerms), ++ false, ++ true); ++ ++ /* 4th argument : abort/filter mode */ ++ abort = (sepgsql_rowlv_mode != SEPGSQL_ROWLV_FILTER); ++ c4 = makeConst(BOOLOID, ++ -1, ++ sizeof(bool), ++ BoolGetDatum(abort), ++ false, ++ true); ++ ++ /* sepgsql_tuple_perms(tableoid, , ) */ ++ func = makeFuncExpr(F_SEPGSQL_TUPLE_PERMS, ++ BOOLOID, ++ list_make4(v1, v2, c3, c4), ++ COERCE_DONTCARE); ++ ++ /* append row-level access control policy */ ++ if (abort) ++ scan->plan.qual = lappend(scan->plan.qual, func); ++ else ++ scan->plan.qual = lcons(func, scan->plan.qual); ++ } ++#endif ++} ++ ++uint32 ++sepgsql_rowlv_permissions(RangeTblEntry *rte) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ uint32 required = 0; ++ ++ if (!OidIsValid(rte->relid) || ++ get_rel_relkind(rte->relid) != RELKIND_RELATION) ++ return 0; ++ ++ if (rte->requiredPerms & ACL_SELECT) ++ required |= SEPG_DB_TUPLE__SELECT; ++ ++ if (rte->requiredPerms & ACL_UPDATE && ++ !bms_is_empty(rte->modifiedCols)) ++ required |= SEPG_DB_TUPLE__UPDATE; ++ ++ if (rte->requiredPerms & ACL_DELETE) ++ required |= SEPG_DB_TUPLE__DELETE; ++ ++ return required; ++ } ++#endif ++ return 0; ++} ++ ++void ++sepgsql_tuple_insert(Relation rel, HeapTuple tuple) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ Oid relOid = RelationGetRelid(rel); ++ ++ if (!RelationGetForm(rel)->relhassecids) ++ { ++ nsid.relid = RelationRelationId; ++ nsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); ++ } ++ else if (OidIsValid(HeapTupleGetSecid(tuple))) ++ { ++ nsid.relid = relOid; ++ nsid.secid = HeapTupleGetSecid(tuple); ++ } ++ else ++ { ++ nsid = sepgsql_get_default_tuple_secid(relOid); ++ HeapTupleSetSecid(tuple, nsid.secid); ++ } ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ NULL, ++ true); ++ } ++#endif ++} ++ ++void ++sepgsql_tuple_update(Relation rel, ItemPointer otid, HeapTuple newtup) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ HeapTupleData oldtup; ++ Buffer oldbuf; ++ Oid newSecId = HeapTupleGetSecid(newtup); ++ Oid oldSecId; ++ ++ /* ++ * heap_update() preserves security id of the original tuple, ++ * if no explicit security label was given, so we don't need ++ * to check anything. ++ * At this point, db_tuple:{update} is already checked. ++ */ ++ if (!OidIsValid(newSecId)) ++ return; ++ ++ /* ++ * User gave an explicit security label ++ */ ++ ItemPointerCopy(otid, &oldtup.t_self); ++ if (!heap_fetch(rel, SnapshotAny, &oldtup, &oldbuf, false, NULL)) ++ elog(ERROR, "failed to fetch old version of the tuple"); ++ ++ tsid.relid = RelationGetRelid(rel); ++ oldSecId = HeapTupleGetSecid(&oldtup); ++ ++ if (!seclabelCompareSecid(tsid.relid, oldSecId, ++ tsid.relid, newSecId)) ++ { ++ /* db_tuple:{relabelfrom} */ ++ tsid.secid = oldSecId; ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELFROM, ++ NULL, ++ true); ++ ++ /* db_tuple:{relabelto} */ ++ tsid.secid = newSecId; ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELTO, ++ NULL, ++ true); ++ } ++ ReleaseBuffer(oldbuf); ++ } ++#endif ++} ++ ++Datum ++sepgsql_tuple_perms(PG_FUNCTION_ARGS) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Form_pg_class relForm; ++ Form_pg_attribute attForm; ++ sepgsql_sid_t tsid; ++ HeapTupleHeader htup; ++ HeapTupleData tuple; ++ uint16 tclass; ++ uint32 required; ++ Oid tableOid; ++ bool abort; ++ bool result; ++ ++ /* function arguments */ ++ tableOid = PG_GETARG_OID(0); ++ htup = PG_GETARG_HEAPTUPLEHEADER(1); ++ required = PG_GETARG_UINT32(2); ++ abort = PG_GETARG_BOOL(3); ++ ++ /* set up pseudo tuple */ ++ tuple.t_len = HeapTupleHeaderGetDatumLength(htup); ++ ItemPointerSetInvalid(&(tuple.t_self)); ++ tuple.t_tableOid = tableOid; ++ tuple.t_data = htup; ++ ++ /* object class? */ ++ switch (tableOid) ++ { ++ case DatabaseRelationId: ++ tclass = SEPG_CLASS_DB_DATABASE; ++ break; ++ ++ case NamespaceRelationId: ++ tclass = SEPG_CLASS_DB_SCHEMA; ++ break; ++ ++ case RelationRelationId: ++ relForm = (Form_pg_class) GETSTRUCT(&tuple); ++ switch (relForm->relkind) ++ { ++ case RELKIND_RELATION: ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ break; ++ case RELKIND_COMPOSITE_TYPE: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ default: /* index, toast */ ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ } ++ break; ++ ++ case AttributeRelationId: ++ attForm = (Form_pg_attribute) GETSTRUCT(&tuple); ++ switch (get_rel_relkind(attForm->attrelid)) ++ { ++ case RELKIND_RELATION: ++ tclass = SEPG_CLASS_DB_COLUMN; ++ break; ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ break; ++ case RELKIND_COMPOSITE_TYPE: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ default: /* index, toast */ ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ } ++ break; ++ ++ case LanguageRelationId: ++ tclass = SEPG_CLASS_DB_LANGUAGE; ++ break; ++ ++ case LargeObjectMetadataRelationId: ++ tclass = SEPG_CLASS_DB_BLOB; ++ break; ++ ++ default: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ } ++ ++ /* do permission check */ ++ tsid.relid = tableOid; ++ tsid.secid = HeapTupleGetSecid(&tuple); ++ ++ result = sepgsql_client_perms(tsid, tclass, required, NULL, abort); ++ ++ PG_RETURN_BOOL(result); ++ } ++#endif ++ PG_RETURN_BOOL(true); ++} +diff --git a/src/backend/sepgsql/schema.c b/src/backend/sepgsql/schema.c +new file mode 100644 +index 0000000..24878f9 +--- /dev/null ++++ b/src/backend/sepgsql/schema.c +@@ -0,0 +1,173 @@ ++/* ++ * schema.c ++ * ++ * SELinux hooks related to schema ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_seclabel.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_schema_common(Oid namespaceOid, uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(NAMESPACEOID, ++ ObjectIdGetDatum(namespaceOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for namespace %u", namespaceOid); ++ ++ tsid.relid = NamespaceRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_SCHEMA, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_schema_create(const char *nspName, bool is_temp) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ /* compute default security context */ ++ nsid = sepgsql_get_default_schema_secid(MyDatabaseId); ++ ++ /* db_schema:{create} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_SCHEMA, ++ SEPG_DB_SCHEMA__CREATE, ++ nspName, true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_schema_alter(Oid namespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__SETATTR, ++ true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_schema_relabel(Oid namespaceOid, char *new_label) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char *auname; ++ ++ nsid.relid = NamespaceRelationId; ++ nsid.secid = seclabelTransInput(nsid.relid, new_label); ++ ++ auname = get_namespace_name(namespaceOid); ++ ++ /* db_schema:{setattr relabelfrom} */ ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__SETATTR | ++ SEPG_DB_SCHEMA__RELABELFROM, ++ true); ++ ++ /* db_schema:{relabelto} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_SCHEMA, ++ SEPG_DB_SCHEMA__RELABELTO, ++ auname, ++ true); ++ pfree(auname); ++ ++ return nsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_schema_drop(Oid namespaceOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__DROP, ++ true); ++ } ++#endif ++} ++ ++void ++sepgsql_schema_grant(Oid namespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__SETATTR, ++ true); ++ } ++#endif ++} ++ ++bool ++sepgsql_schema_search(Oid namespaceOid, bool abort) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ bool retval = ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__SEARCH, ++ abort); ++ return retval; ++ } ++#endif ++ return true; ++} ++ ++void ++sepgsql_schema_comment(Oid namespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__SETATTR, ++ true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/selinux.c b/src/backend/sepgsql/selinux.c +new file mode 100644 +index 0000000..ea9c9da +--- /dev/null ++++ b/src/backend/sepgsql/selinux.c +@@ -0,0 +1,670 @@ ++/* ++ * src/backend/security/sepgsql/selinux.c ++ * Routines to communicate with SELinux. ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_seclabel.h" ++#include "libpq/libpq.h" ++#include "miscadmin.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/guc.h" ++#include "utils/memutils.h" ++ ++#include ++ ++/* ++ * selinux_catalog ++ * ++ * This static translation lookup table enables to associate a certain ++ * object class/permission name with its internal code, such as ++ * SEPG_CLASS_DB_SCHEMA. ++ * ++ * SELinux requires applications to represent object class and a set of ++ * permissions in code, instead of its name, when we ask SELinux's decision. ++ * ++ * See the definition of security_compute_av(3) API in libselinux. ++ * We need to gives a code of object class, and interpret what permissions ++ * are allowed on the object class from av_decision structure. ++ * Actual values of the code depend on the security policy. In other words, ++ * we cannot know what number is assigned on a certain object class and ++ * permissions. ++ * The string_to_security_class(3) and string_to_av_perm(3) APIs takes ++ * arguments with the name of object class/permission, and returns the ++ * code for the given object class/permissions. ++ * For example, we can know what code is assigned on the "db_table" class ++ * using these functions as follows: ++ * ++ * uint16 tclass_ex = string_to_security_class("db_table"); ++ * ++ * On the other hand, we use an alternative code internally to simplify ++ * the implementation, such as SEPG_CLASS_* for object class. ++ * The following selinux_catalog is used to translate the 'internal' ++ * code and the 'external' code. ++ * ++ * It allows to lookup name of the object class or permission corresponding ++ * to a certain 'internal' code. Then, we can give the name to SELinux's ++ * API to obtain 'external' code which can be used to ask in-kernel SELinux. ++ */ ++static struct ++{ ++ const char *class_name; ++ uint16 class_code; ++ struct ++ { ++ const char *perm_name; ++ uint32 perm_code; ++ } perms[32]; ++} selinux_catalog[] = { ++ { ++ "process", SEPG_CLASS_PROCESS, ++ { ++ { "translation", SEPG_PROCESS__TRANSITION }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "file", SEPG_CLASS_FILE, ++ { ++ { "read", SEPG_FILE__READ }, ++ { "write", SEPG_FILE__WRITE }, ++ { "create", SEPG_FILE__CREATE }, ++ { "getattr", SEPG_FILE__GETATTR }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "dir", SEPG_CLASS_DIR, ++ { ++ { "read", SEPG_DIR__READ }, ++ { "write", SEPG_DIR__WRITE }, ++ { "create", SEPG_DIR__CREATE }, ++ { "getattr", SEPG_DIR__GETATTR }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "lnk_file", SEPG_CLASS_LNK_FILE, ++ { ++ { "read", SEPG_LNK_FILE__READ }, ++ { "write", SEPG_LNK_FILE__WRITE }, ++ { "create", SEPG_LNK_FILE__CREATE }, ++ { "getattr", SEPG_LNK_FILE__GETATTR }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "chr_file", SEPG_CLASS_CHR_FILE, ++ { ++ { "read", SEPG_CHR_FILE__READ }, ++ { "write", SEPG_CHR_FILE__WRITE }, ++ { "create", SEPG_CHR_FILE__CREATE }, ++ { "getattr", SEPG_CHR_FILE__GETATTR }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "blk_file", SEPG_CLASS_BLK_FILE, ++ { ++ { "read", SEPG_BLK_FILE__READ }, ++ { "write", SEPG_BLK_FILE__WRITE }, ++ { "create", SEPG_BLK_FILE__CREATE }, ++ { "getattr", SEPG_BLK_FILE__GETATTR }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "sock_file", SEPG_CLASS_SOCK_FILE, ++ { ++ { "read", SEPG_SOCK_FILE__READ }, ++ { "write", SEPG_SOCK_FILE__WRITE }, ++ { "create", SEPG_SOCK_FILE__CREATE }, ++ { "getattr", SEPG_SOCK_FILE__GETATTR }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "fifo_file", SEPG_CLASS_FIFO_FILE, ++ { ++ { "read", SEPG_FIFO_FILE__READ }, ++ { "write", SEPG_FIFO_FILE__WRITE }, ++ { "create", SEPG_FIFO_FILE__CREATE }, ++ { "getattr", SEPG_FIFO_FILE__GETATTR }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "db_database", SEPG_CLASS_DB_DATABASE, ++ { ++ { "create", SEPG_DB_DATABASE__CREATE }, ++ { "drop", SEPG_DB_DATABASE__DROP }, ++ { "getattr", SEPG_DB_DATABASE__GETATTR }, ++ { "setattr", SEPG_DB_DATABASE__SETATTR }, ++ { "relabelfrom", SEPG_DB_DATABASE__RELABELFROM }, ++ { "relabelto", SEPG_DB_DATABASE__RELABELTO }, ++ { "access", SEPG_DB_DATABASE__ACCESS }, ++ { "load_module", SEPG_DB_DATABASE__LOAD_MODULE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_schema", SEPG_CLASS_DB_SCHEMA, ++ { ++ { "create", SEPG_DB_SCHEMA__CREATE }, ++ { "drop", SEPG_DB_SCHEMA__DROP }, ++ { "getattr", SEPG_DB_SCHEMA__GETATTR }, ++ { "setattr", SEPG_DB_SCHEMA__SETATTR }, ++ { "relabelfrom", SEPG_DB_SCHEMA__RELABELFROM }, ++ { "relabelto", SEPG_DB_SCHEMA__RELABELTO }, ++ { "search", SEPG_DB_SCHEMA__SEARCH }, ++ { "add_name", SEPG_DB_SCHEMA__ADD_NAME }, ++ { "remove_name", SEPG_DB_SCHEMA__REMOVE_NAME }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_table", SEPG_CLASS_DB_TABLE, ++ { ++ { "create", SEPG_DB_TABLE__CREATE }, ++ { "drop", SEPG_DB_TABLE__DROP }, ++ { "getattr", SEPG_DB_TABLE__GETATTR }, ++ { "setattr", SEPG_DB_TABLE__SETATTR }, ++ { "relabelfrom", SEPG_DB_TABLE__RELABELFROM }, ++ { "relabelto", SEPG_DB_TABLE__RELABELTO }, ++ { "select", SEPG_DB_TABLE__SELECT }, ++ { "update", SEPG_DB_TABLE__UPDATE }, ++ { "insert", SEPG_DB_TABLE__INSERT }, ++ { "delete", SEPG_DB_TABLE__DELETE }, ++ { "lock", SEPG_DB_TABLE__LOCK }, ++ { "indexon", SEPG_DB_TABLE__INDEXON }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_view", SEPG_CLASS_DB_VIEW, ++ { ++ { "create", SEPG_DB_VIEW__CREATE }, ++ { "drop", SEPG_DB_VIEW__DROP }, ++ { "getattr", SEPG_DB_VIEW__GETATTR }, ++ { "setattr", SEPG_DB_VIEW__SETATTR }, ++ { "relabelfrom", SEPG_DB_VIEW__RELABELFROM }, ++ { "relabelto", SEPG_DB_VIEW__RELABELTO }, ++ { "expand", SEPG_DB_VIEW__EXPAND }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_sequence", SEPG_CLASS_DB_SEQUENCE, ++ { ++ { "create", SEPG_DB_SEQUENCE__CREATE }, ++ { "drop", SEPG_DB_SEQUENCE__DROP }, ++ { "getattr", SEPG_DB_SEQUENCE__GETATTR }, ++ { "setattr", SEPG_DB_SEQUENCE__SETATTR }, ++ { "relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM }, ++ { "relabelto", SEPG_DB_SEQUENCE__RELABELTO }, ++ { "get_value", SEPG_DB_SEQUENCE__GET_VALUE }, ++ { "next_value", SEPG_DB_SEQUENCE__NEXT_VALUE }, ++ { "set_value", SEPG_DB_SEQUENCE__SET_VALUE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_procedure", SEPG_CLASS_DB_PROCEDURE, ++ { ++ { "create", SEPG_DB_PROCEDURE__CREATE }, ++ { "drop", SEPG_DB_PROCEDURE__DROP }, ++ { "getattr", SEPG_DB_PROCEDURE__GETATTR }, ++ { "setattr", SEPG_DB_PROCEDURE__SETATTR }, ++ { "relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM }, ++ { "relabelto", SEPG_DB_PROCEDURE__RELABELTO }, ++ { "execute", SEPG_DB_PROCEDURE__EXECUTE }, ++ { "entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT }, ++ { "install", SEPG_DB_PROCEDURE__INSTALL }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_column", SEPG_CLASS_DB_COLUMN, ++ { ++ { "create", SEPG_DB_COLUMN__CREATE }, ++ { "drop", SEPG_DB_COLUMN__DROP }, ++ { "getattr", SEPG_DB_COLUMN__GETATTR }, ++ { "setattr", SEPG_DB_COLUMN__SETATTR }, ++ { "relabelfrom", SEPG_DB_COLUMN__RELABELFROM }, ++ { "relabelto", SEPG_DB_COLUMN__RELABELTO }, ++ { "select", SEPG_DB_COLUMN__SELECT }, ++ { "update", SEPG_DB_COLUMN__UPDATE }, ++ { "insert", SEPG_DB_COLUMN__INSERT }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_tuple", SEPG_CLASS_DB_TUPLE, ++ { ++ { "relabelfrom", SEPG_DB_TUPLE__RELABELFROM }, ++ { "relabelto", SEPG_DB_TUPLE__RELABELTO }, ++ { "select", SEPG_DB_TUPLE__SELECT }, ++ { "update", SEPG_DB_TUPLE__UPDATE }, ++ { "insert", SEPG_DB_TUPLE__INSERT }, ++ { "delete", SEPG_DB_TUPLE__DELETE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_blob", SEPG_CLASS_DB_BLOB, ++ { ++ { "create", SEPG_DB_BLOB__CREATE }, ++ { "drop", SEPG_DB_BLOB__DROP }, ++ { "getattr", SEPG_DB_BLOB__GETATTR }, ++ { "setattr", SEPG_DB_BLOB__SETATTR }, ++ { "relabelfrom", SEPG_DB_BLOB__RELABELFROM }, ++ { "relabelto", SEPG_DB_BLOB__RELABELTO }, ++ { "read", SEPG_DB_BLOB__READ }, ++ { "write", SEPG_DB_BLOB__WRITE }, ++ { "import", SEPG_DB_BLOB__IMPORT }, ++ { "export", SEPG_DB_BLOB__EXPORT }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_language", SEPG_CLASS_DB_LANGUAGE, ++ { ++ { "create", SEPG_DB_LANGUAGE__CREATE }, ++ { "drop", SEPG_DB_LANGUAGE__DROP }, ++ { "getattr", SEPG_DB_LANGUAGE__GETATTR }, ++ { "setattr", SEPG_DB_LANGUAGE__SETATTR }, ++ { "relabelfrom", SEPG_DB_LANGUAGE__RELABELFROM }, ++ { "relabelto", SEPG_DB_LANGUAGE__RELABELTO }, ++ { "implement", SEPG_DB_LANGUAGE__IMPLEMENTE }, ++ { "execute", SEPG_DB_LANGUAGE__EXECUTE }, ++ { NULL, 0UL }, ++ } ++ }, ++}; ++ ++/* ++ * GUC option: sepostgresql = [default|enforcing|permissive|disabled] ++ * ++ * SEPGSQL_MODE_DEFAULT : It follows system setting ++ * SEPGSQL_MODE_ENFORCING : Use enforcing mode always ++ * SEPGSQL_MODE_PERMISSIVE : Use permissive mode always ++ * SEPGSQL_MODE_INTERNAL : Internally used mode. Same as permissive mode ++ * except for silence in audit logs ++ * SEPGSQL_MODE_DISABLED : It always disables SE-PgSQL configuration ++ */ ++int sepostgresql_mode; ++ ++/* ++ * sepgsql_is_enabled ++ * ++ * If it returns true, SE-PgSQL is enabled. Otherwise, it is disabled. ++ */ ++bool ++sepgsql_is_enabled(void) ++{ ++ static int enabled = -1; ++ ++ /* ++ * If sepostgresql = disabled, it always returns FALSE ++ * independently from the system status. ++ */ ++ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED) ++ return false; ++ ++ /* ++ * SE-PgSQL needs SELinux is enabled on the operating system. ++ * If it is disabled, SE-PgSQL has to be also disabled, even if ++ * 'enforcing' or 'permissive' are specified. ++ */ ++ if (enabled < 0) ++ enabled = is_selinux_enabled(); ++ ++ return enabled > 0 ? true : false; ++} ++ ++/* ++ * sepgsql_get_enforce ++ * ++ * It returns true, if SE-PgSQL performs in enforcing mode. ++ * ++ * In enforcing mode, SE-PgSQL performs as expected. It checks permissions ++ * on the required action, and it prevents them if violated. ++ * In permissive mode, SE-PgSQL also checks permissions, but it does not ++ * prevent anything, even if violated. It generates audit logs for access ++ * violations, so we can use this mode to debug security policy itself. ++ */ ++bool ++sepgsql_get_enforce(void) ++{ ++ if (sepostgresql_mode == SEPGSQL_MODE_DEFAULT) ++ { ++ if (security_getenforce() == 1) ++ return true; ++ } ++ else if (sepostgresql_mode == SEPGSQL_MODE_ENFORCING) ++ return true; ++ ++ return false; ++} ++ ++/* ++ * sepgsql_show_mode ++ * ++ * It returns the current performing mode ('selinux_support') ++ * in human readable form. ++ */ ++const char * ++sepgsql_show_mode(void) ++{ ++ if (!sepgsql_is_enabled()) ++ return "disabled"; ++ ++ if (!sepgsql_get_enforce()) ++ return "permissive"; ++ ++ return "enforcing"; ++} ++ ++/* ++ * GUC parameter to turn on/off debuging audit generation ++ */ ++bool sepgsql_debug_audit; ++ ++/* ++ * sepgsql_audit_log ++ * ++ * It generates a security audit record. In the default, it writes out ++ * audit records into standard PG's logfile. It also allows to set up ++ * external audit log receiver, such as auditd in Linux, using the ++ * sepgsql_audit_hook. ++ * ++ * SELinux can control what should be audited and should not using ++ * "auditdeny" and "auditallow" rules in the security policy. In the ++ * default, all the access violations are audited, and all the access ++ * allowed are not audited. But we can set up the security policy, so ++ * we can have exceptions. So, it is necessary to follow the suggestion ++ * come from the security policy. (av_decision.auditallow and auditdeny) ++ * ++ * Security audit is an important feature, because it enables us to check ++ * what was happen if we have a security incident. In fact, ISO/IEC15408 ++ * defines several security functionalities for audit features. ++ */ ++void ++sepgsql_audit_log(bool denied, char *scontext, char *tcontext, ++ uint16 tclass, uint32 audited, const char *audit_name) ++{ ++ StringInfoData buf; ++ const char *tclass_name; ++ const char *perm_name; ++ int level = LOG; ++ int i; ++ ++ /* ++ * translation of security contexts to human readable format, ++ * if sepgsql_mcstrans is turned on. ++ */ ++ scontext = sepgsql_mcstrans_out(scontext); ++ tcontext = sepgsql_mcstrans_out(tcontext); ++ ++ /* lookup name of the object class */ ++ tclass_name = selinux_catalog[tclass].class_name; ++ ++ /* lookup name of the permissions */ ++ initStringInfo(&buf); ++ appendStringInfo(&buf, "{"); ++ ++ for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++) ++ { ++ if (audited & (1UL << i)) ++ { ++ perm_name = selinux_catalog[tclass].perms[i].perm_name; ++ appendStringInfo(&buf, " %s", perm_name); ++ } ++ } ++ appendStringInfo(&buf, " }"); ++ ++ /* ++ * Call external audit module, if loaded ++ */ ++ appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, tclass_name); ++ if (audit_name) ++ appendStringInfo(&buf, " name=%s", audit_name); ++ ++ if (sepgsql_debug_audit) ++ level = client_min_messages; ++ ++ ereport(level, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("SELinux: %s %s", ++ (denied ? "denied" : "allowed"), buf.data))); ++} ++ ++/* ++ * sepgsql_compute_avd ++ * ++ * It actually asks SELinux what permissions are allowed on a pair of ++ * the security contexts and object class. It also returns what permissions ++ * should be audited on access violation or allowed. ++ * In most cases, subject's security context (scontext) is a client, and ++ * target security context (tcontext) is a database object. ++ * ++ * The access control decision shall be set on the given av_decision. ++ * The av_decision.allowed has a bitmask of SEPG___ ++ * to suggest a set of allowed actions in this object class. ++ */ ++void ++sepgsql_compute_avd(char *scontext, char *tcontext, ++ uint16 tclass, struct av_decision *avd) ++{ ++ const char *tclass_name; ++ security_class_t tclass_ex; ++ struct av_decision avd_ex; ++ int i, deny_unknown = security_deny_unknown(); ++ ++ /* Get external code of the object class*/ ++ Assert(tclass < SEPG_CLASS_MAX); ++ ++ tclass_name = selinux_catalog[tclass].class_name; ++ tclass_ex = string_to_security_class(tclass_name); ++ ++ if (tclass_ex == 0) ++ { ++ /* ++ * If the current security policy does not support permissions ++ * corresponding to database objects, we fill up them with dummy ++ * data. ++ * If security_deny_unknown() returns positive value, undefined ++ * permissions should be denied. Otherwise, allowed ++ */ ++ avd->allowed = (deny_unknown > 0 ? 0 : ~0U); ++ avd->auditallow = 0U; ++ avd->auditdeny = ~0U; ++ avd->flags = 0; ++ ++ return; ++ } ++ ++ /* ++ * Ask SELinux what is allowed set of permissions on a pair of the ++ * security contexts and the given object class. ++ */ ++ if (security_compute_av_flags_raw(scontext, tcontext, ++ tclass_ex, 0, &avd_ex) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux could not compute av_decision: " ++ "scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, tclass_name))); ++ ++ /* ++ * SELinux returns its access control decision as a set of permissions ++ * represented in external code which depends on run-time environment. ++ * So, we need to translate it to the internal representation before ++ * returning results for the caller. ++ */ ++ memset(avd, 0, sizeof(struct av_decision)); ++ ++ for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++) ++ { ++ access_vector_t perm_code_ex; ++ const char *perm_name = selinux_catalog[tclass].perms[i].perm_name; ++ uint32 perm_code = selinux_catalog[tclass].perms[i].perm_code; ++ ++ perm_code_ex = string_to_av_perm(tclass_ex, perm_name); ++ if (perm_code_ex == 0) ++ { ++ /* fill up undefined permissions */ ++ if (!deny_unknown) ++ avd->allowed |= perm_code; ++ avd->auditdeny |= perm_code; ++ ++ continue; ++ } ++ ++ if (avd_ex.allowed & perm_code_ex) ++ avd->allowed |= perm_code; ++ if (avd_ex.auditallow & perm_code_ex) ++ avd->auditallow |= perm_code; ++ if (avd_ex.auditdeny & perm_code_ex) ++ avd->auditdeny |= perm_code; ++ } ++ ++ return; ++} ++ ++/* ++ * sepgsql_compute_perms ++ * ++ * It makes access control decision communicating with SELinux. ++ * If SELinux does not allow required permissions on a pair of the security ++ * contexts, it raises an error or returns false. ++ * ++ * scontext : The security context of subject. In most cases, it is client. ++ * tcontext : The security context of target database object. ++ * tclass : One of the object class code (SEPG_CLASS_*) declared in the ++ * header file. ++ * required : A bitmap of the required permissions (SEPG___) ++ * declared in the header file. ++ * audit_name : A human readable name of the database object for auditing. ++ * abort : True, if caller want to raise an error on access violation. ++ */ ++bool ++sepgsql_compute_perms(char *scontext, char *tcontext, ++ uint16 tclass, uint32 required, ++ const char *audit_name, bool abort) ++{ ++ struct av_decision avd; ++ uint32 denied; ++ uint32 audited; ++ ++ sepgsql_compute_avd(scontext, tcontext, tclass, &avd); ++ ++ /* ++ * It logs a security audit record for the given request, if necessary. ++ * When SE-PgSQL performs 'internal' mode, it needs to keep silent. ++ */ ++ denied = required & ~avd.allowed; ++ if (sepgsql_debug_audit && tclass != SEPG_CLASS_DB_TUPLE) ++ audited = (denied ? (denied & ~0) : (required & ~0)); ++ else ++ audited = (denied ? (denied & avd.auditdeny) ++ : (required & avd.auditallow)); ++ ++ if (audited && sepostgresql_mode != SEPGSQL_MODE_INTERNAL) ++ { ++ sepgsql_audit_log(!!denied, scontext, tcontext, ++ tclass, audited, audit_name); ++ } ++ ++ /* ++ * If here is no policy violations, or SE-PgSQL performs in permissive ++ * mode, or the client process peforms in permissive domain, it returns ++ * normally with 'true'. ++ */ ++ if (!denied || ++ !sepgsql_get_enforce() || ++ (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) != 0) ++ return true; ++ ++ /* ++ * Otherwise, it raises an error or returns 'false', depending on the ++ * caller's indication by 'abort'. ++ */ ++ if (abort) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("SELinux: security policy violation"))); ++ ++ return false; ++} ++ ++/* ++ * sepgsql_compute_create ++ * ++ * It returns a default security context to be assigned on a new database ++ * object. SELinux compute it based on a combination of client, upper object ++ * which owns the new object and object class. ++ * ++ * For example, when a client (staff_u:staff_r:staff_t:s0) tries to create ++ * a new table within a schema (system_u:object_r:sepgsql_schema_t:s0), ++ * SELinux looks-up its security policy. If it has a special rule on the ++ * combination of these security contexts and object class (db_table), ++ * it returns the security context suggested by the special rule. ++ * Otherwise, it returns the security context of schema, as is. ++ * ++ * We expect the caller already applies sanity/validation checks on the ++ * given security context. ++ * ++ * scontext : The security context of subject. In most cases, it is client. ++ * tcontext : The security context of the parent database object.. ++ * tclass : One of the object class code (SEPG_CLASS_*) declared in the ++ * header file. ++ */ ++char * ++sepgsql_compute_create(char *scontext, char *tcontext, uint16 tclass) ++{ ++ security_context_t ncontext; ++ security_class_t tclass_ex; ++ const char *tclass_name; ++ char *result; ++ ++ /* Get external code of the object class*/ ++ Assert(tclass < SEPG_CLASS_MAX); ++ ++ tclass_name = selinux_catalog[tclass].class_name; ++ tclass_ex = string_to_security_class(tclass_name); ++ ++ /* ++ * Ask SELinux what is the default context for the given object class ++ * on a pair of security contexts ++ */ ++ if (security_compute_create_raw(scontext, tcontext, ++ tclass_ex, &ncontext)) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux could not compute a new context: " ++ "scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, tclass_name))); ++ ++ /* ++ * libselinux returns malloc()'ed string, so we need to copy it ++ * on the palloc()'ed region. ++ */ ++ PG_TRY(); ++ { ++ result = pstrdup(ncontext); ++ } ++ PG_CATCH(); ++ { ++ freecon(ncontext); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(ncontext); ++ ++ return result; ++} +diff --git a/src/backend/sepgsql/tablespace.c b/src/backend/sepgsql/tablespace.c +new file mode 100644 +index 0000000..1d3a35e +--- /dev/null ++++ b/src/backend/sepgsql/tablespace.c +@@ -0,0 +1,157 @@ ++/* ++ * tablespace.c ++ * ++ * SELinux hooks related to tablespaces ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_tablespace.h" ++#include "catalog/pg_seclabel.h" ++#include "commands/tablespace.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/syscache.h" ++#include "utils/lsyscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_tablespace_common(Oid tablespaceOid, uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ bool retval; ++ const char *auname; ++ ++ tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(tablespaceOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for tablespace %u", tablespaceOid); ++ ++ tsid.relid = TableSpaceRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname); ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++#endif ++ ++Oid ++sepgsql_tablespace_create(const char *tablespaceName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ nsid = sepgsql_get_default_tuple_secid(TableSpaceRelationId); ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ tablespaceName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_tablespace_alter(Oid tablespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_tablespace_common(tablespaceOid, ++ SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_tablespace_relabel(Oid tablespaceOid, char *newLabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ ++ tsid.relid = TableSpaceRelationId; ++ tsid.secid = seclabelTransInput(tsid.relid, newLabel); ++ ++ /* db_tuple:{update relabelfrom} */ ++ sepgsql_tablespace_common(tablespaceOid, ++ SEPG_DB_TUPLE__UPDATE | ++ SEPG_DB_TUPLE__RELABELFROM, true); ++ ++ /* db_procedure:{relabelto} */ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELTO, ++ get_tablespace_name(tablespaceOid), ++ true); ++ return tsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_tablespace_drop(Oid tablespaceOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_tablespace_common(tablespaceOid, ++ SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_tablespace_grant(Oid tablespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_tablespace_common(tablespaceOid, ++ SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_tablespace_getattr(Oid tablespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_tablespace_common(tablespaceOid, ++ SEPG_DB_TUPLE__SELECT, true); ++ } ++#endif ++} ++ ++void ++sepgsql_tablespace_comment(Oid tablespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_tablespace_common(tablespaceOid, ++ SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/tsearch.c b/src/backend/sepgsql/tsearch.c +new file mode 100644 +index 0000000..3d4180d +--- /dev/null ++++ b/src/backend/sepgsql/tsearch.c +@@ -0,0 +1,524 @@ ++/* ++ * tsearch.c ++ * ++ * SELinux hooks related to text searches ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_ts_config.h" ++#include "catalog/pg_ts_dict.h" ++#include "catalog/pg_ts_parser.h" ++#include "catalog/pg_ts_template.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_ts_config_common(Oid confOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(confOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search configuration %u", confOid); ++ ++ tsid.relid = TSConfigRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_ts_config) GETSTRUCT(tuple))->cfgname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_ts_config_namespace(Oid confOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(confOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++bool ++sepgsql_ts_dict_common(Oid dictOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); ++ ++ tsid.relid = TSDictionaryRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_ts_dict_namespace(Oid dictOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++bool ++sepgsql_ts_parser_common(Oid parseOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(parseOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search parser %u", parseOid); ++ ++ tsid.relid = TSParserRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_ts_parser) GETSTRUCT(tuple))->prsname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_ts_parser_namespace(Oid parseOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(parseOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++bool ++sepgsql_ts_template_common(Oid templateOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(templateOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search template %u", templateOid); ++ ++ tsid.relid = TSDictionaryRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_ts_template_namespace(Oid templateOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(templateOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++#endif ++ ++Oid ++sepgsql_ts_config_create(const char *confName, Oid namespaceId) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(TSConfigRelationId); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ confName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_ts_config_alter(Oid confOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_config_alter_rename(Oid confOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_config_namespace(confOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_config_drop(Oid confOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_config_namespace(confOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_config_comment(Oid confOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_ts_dict_create(const char *dictName, Oid namespaceId) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(TSDictionaryRelationId); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ dictName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_ts_dict_alter(Oid dictOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_dict_alter_rename(Oid dictOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_dict_namespace(dictOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_dict_drop(Oid dictOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_dict_namespace(dictOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_dict_comment(Oid dictOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_ts_parser_create(const char *parseName, Oid namespaceId, ++ Oid startFunc, Oid tokenFunc, Oid endFunc, ++ Oid headlineFunc, Oid lextypeFunc) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(TSParserRelationId); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(startFunc)) ++ sepgsql_proc_common(startFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(tokenFunc)) ++ sepgsql_proc_common(tokenFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(endFunc)) ++ sepgsql_proc_common(endFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(headlineFunc)) ++ sepgsql_proc_common(headlineFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(lextypeFunc)) ++ sepgsql_proc_common(lextypeFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ parseName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_ts_parser_alter_rename(Oid parseOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_parser_namespace(parseOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_parser_drop(Oid parseOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_parser_namespace(parseOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_parser_comment(Oid parseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_ts_template_create(const char *templateName, Oid namespaceId, ++ Oid initFunc, Oid lexizeFunc) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(TSTemplateRelationId); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(initFunc)) ++ sepgsql_proc_common(initFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(lexizeFunc)) ++ sepgsql_proc_common(lexizeFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ templateName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_ts_template_alter_rename(Oid templateOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_template_namespace(templateOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_template_drop(Oid templateOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_template_namespace(templateOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_template_comment(Oid templateOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/type.c b/src/backend/sepgsql/type.c +new file mode 100644 +index 0000000..8cd65ae +--- /dev/null ++++ b/src/backend/sepgsql/type.c +@@ -0,0 +1,314 @@ ++/* ++ * type.c ++ * ++ * SELinux hooks related to types ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_cast.h" ++#include "catalog/pg_seclabel.h" ++#include "catalog/pg_type.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/builtins.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_type_common(Oid typeOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ char *auname; ++ bool retval; ++ ++ tsid.relid = TypeRelationId; ++ tsid.secid = GetSysCacheSecid1(TYPEOID, ++ ObjectIdGetDatum(typeOid)); ++ ++ auname = format_type_be(typeOid); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ pfree(auname); ++ ++ return retval; ++} ++ ++bool ++sepgsql_cast_common(Oid srcTypeOid, Oid dstTypeOid, ++ uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ bool retval; ++ ++ tuple = SearchSysCache2(CASTSOURCETARGET, ++ ObjectIdGetDatum(srcTypeOid), ++ ObjectIdGetDatum(dstTypeOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for cast (%u,%u)", ++ srcTypeOid, dstTypeOid); ++ ++ tsid.relid = CastRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ NULL, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_type_namespace(Oid typeOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId; ++ ++ tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid)); ++ if (!HeapTupleIsValid(tuple)) ++ return InvalidOid; ++ ++ namespaceId = ((Form_pg_type) GETSTRUCT(tuple))->typnamespace; ++ ++ ReleaseSysCache(tuple); ++ ++ return namespaceId; ++} ++#endif ++ ++Oid ++sepgsql_type_create(const char *typeName, Oid replaced, ++ Oid namespaceId, char typeType, ++ Oid inputFunc, Oid outputFunc, ++ Oid recvFunc, Oid sendFunc, ++ Oid modinFunc, Oid modoutFunc, Oid analyzeFunc) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ uint32 required; ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_tuple:{insert or update} */ ++ if (!OidIsValid(replaced)) ++ { ++ nsid = sepgsql_get_default_tuple_secid(TypeRelationId); ++ required = SEPG_DB_TUPLE__INSERT; ++ } ++ else ++ { ++ nsid.relid = TypeRelationId; ++ nsid.secid = GetSysCacheSecid1(TYPEOID, ++ ObjectIdGetDatum(replaced)); ++ required = SEPG_DB_TUPLE__UPDATE; ++ } ++ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ typeName, ++ true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(inputFunc)) ++ sepgsql_proc_common(inputFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(outputFunc)) ++ sepgsql_proc_common(outputFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(recvFunc)) ++ sepgsql_proc_common(recvFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(sendFunc)) ++ sepgsql_proc_common(sendFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(modinFunc)) ++ sepgsql_proc_common(modinFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(modoutFunc)) ++ sepgsql_proc_common(modoutFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(analyzeFunc)) ++ sepgsql_proc_common(analyzeFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_type_alter(Oid typeOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_type_alter_rename(Oid typeOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_type_namespace(typeOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_type_alter_schema(Oid typeOid, Oid newSchema) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_type_namespace(typeOid); ++ ++ /* db_schema:{ remove_name } */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_schema:{ add_name } */ ++ sepgsql_schema_common(newSchema, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_type_relabel(Oid typeOid, char *newLabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ ++ tsid.relid = TypeRelationId; ++ tsid.secid = seclabelTransInput(tsid.relid, newLabel); ++ ++ /* db_tuple:{update relabelfrom} */ ++ sepgsql_type_common(typeOid, ++ SEPG_DB_TUPLE__UPDATE | ++ SEPG_DB_TUPLE__RELABELFROM, true); ++ ++ /* db_procedure:{relabelto} */ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELTO, ++ format_type_be(typeOid), ++ true); ++ return tsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_type_drop(Oid typeOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{delete} */ ++ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_type_comment(Oid typeOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_cast_create(Oid sourceTypeOid, Oid targetTypeOid, ++ char castMethod, Oid castFuncOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ /* db_tuple:{insert} */ ++ nsid = sepgsql_get_default_tuple_secid(CastRelationId); ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ NULL, ++ true); ++ /* db_procedure:{install} */ ++ if (OidIsValid(castFuncOid)) ++ sepgsql_proc_common(castFuncOid, ++ SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_cast_drop(Oid sourceTypeOid, Oid targetTypeOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_cast_common(sourceTypeOid, targetTypeOid, ++ SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_cast_comment(Oid sourceTypeOid, Oid targetTypeOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_cast_common(sourceTypeOid, targetTypeOid, ++ SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} +diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c +index ca48cdd..bb0688f 100644 +--- a/src/backend/storage/large_object/inv_api.c ++++ b/src/backend/storage/large_object/inv_api.c +@@ -197,14 +197,14 @@ getbytealen(bytea *data) + * in use. + */ + Oid +-inv_create(Oid lobjId) ++inv_create(Oid lobjId, Oid securityId) + { + Oid lobjId_new; + + /* + * Create a new largeobject with empty data pages + */ +- lobjId_new = LargeObjectCreate(lobjId); ++ lobjId_new = LargeObjectCreate(lobjId, securityId); + + /* + * dependency on the owner of largeobject +diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c +index 575fa86..eb2b5e8 100644 +--- a/src/backend/tcop/fastpath.c ++++ b/src/backend/tcop/fastpath.c +@@ -26,6 +26,7 @@ + #include "libpq/pqformat.h" + #include "mb/pg_wchar.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "tcop/fastpath.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" +@@ -347,6 +348,10 @@ HandleFunctionRequest(StringInfo msgBuf) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(fid)); + ++ /* SELinux checks */ ++ sepgsql_schema_search(fip->namespace, true); ++ sepgsql_proc_execute(fid); ++ + /* + * Prepare function call info block and insert arguments. + */ +diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c +index dd00b2d..ed76bca 100644 +--- a/src/backend/tcop/postgres.c ++++ b/src/backend/tcop/postgres.c +@@ -699,6 +699,9 @@ pg_rewrite_query(Query *query) + { + /* don't rewrite utilities, just dump 'em into result list */ + querytree_list = list_make1(query); ++ ++ /* SE-PostgreSQL may rewrite the query */ ++ sepgsql_proxy_queries(querytree_list); + } + else + { +diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c +index 8ad4915..54961d8 100644 +--- a/src/backend/tcop/pquery.c ++++ b/src/backend/tcop/pquery.c +@@ -575,7 +575,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) + Assert(pstmt->hasReturning); + portal->tupDesc = + ExecCleanTypeFromTL(pstmt->planTree->targetlist, +- false); ++ false, false); + } + + /* +diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c +index 8960246..e8f8ea3 100644 +--- a/src/backend/tcop/utility.c ++++ b/src/backend/tcop/utility.c +@@ -165,6 +165,7 @@ check_xact_readonly(Node *parsetree) + case T_AlterRoleSetStmt: + case T_AlterObjectSchemaStmt: + case T_AlterOwnerStmt: ++ case T_AlterSecLabelStmt: + case T_AlterSeqStmt: + case T_AlterTableStmt: + case T_RenameStmt: +@@ -696,6 +697,10 @@ standard_ProcessUtility(Node *parsetree, + ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); + break; + ++ case T_AlterSecLabelStmt: ++ ExecAlterSecLabelStmt((AlterSecLabelStmt *) parsetree); ++ break; ++ + case T_AlterTableStmt: + { + List *stmts; +@@ -1760,6 +1765,46 @@ CreateCommandTag(Node *parsetree) + } + break; + ++ case T_AlterSecLabelStmt: ++ switch (((AlterSecLabelStmt *) parsetree)->objectType) ++ { ++ case OBJECT_DATABASE: ++ tag = "ALTER DATABASE"; ++ break; ++ case OBJECT_SCHEMA: ++ tag = "ALTER SCHEMA"; ++ break; ++ case OBJECT_TABLE: ++ case OBJECT_COLUMN: ++ tag = "ALTER TABLE"; ++ break; ++ case OBJECT_SEQUENCE: ++ tag = "ALTER SEQUENCE"; ++ break; ++ case OBJECT_VIEW: ++ tag = "ALTER VIEW"; ++ break; ++ case OBJECT_FUNCTION: ++ tag = "ALTER FUNCTION"; ++ break; ++ case OBJECT_AGGREGATE: ++ tag = "ALTER AGGREGATE"; ++ break; ++ case OBJECT_LARGEOBJECT: ++ tag = "ALTER LARGE OBJECT"; ++ break; ++ case OBJECT_TYPE: ++ tag = "ALTER TYPE"; ++ break; ++ case OBJECT_DOMAIN: ++ tag = "ALTER DOMAIN"; ++ break; ++ default: ++ tag = "???"; ++ break; ++ } ++ break; ++ + case T_AlterTableStmt: + switch (((AlterTableStmt *) parsetree)->relkind) + { +@@ -2352,6 +2397,10 @@ GetCommandLogLevel(Node *parsetree) + lev = LOGSTMT_DDL; + break; + ++ case T_AlterSecLabelStmt: ++ lev = LOGSTMT_DDL; ++ break; ++ + case T_AlterTableStmt: + lev = LOGSTMT_DDL; + break; +diff --git a/src/backend/tsearch/wparser.c b/src/backend/tsearch/wparser.c +index 0fed35c..ca9e9d1 100644 +--- a/src/backend/tsearch/wparser.c ++++ b/src/backend/tsearch/wparser.c +@@ -59,7 +59,7 @@ tt_setup_firstcall(FuncCallContext *funcctx, Oid prsid) + (Datum) 0)); + funcctx->user_fctx = (void *) st; + +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "alias", +@@ -205,7 +205,7 @@ prs_setup_firstcall(FuncCallContext *funcctx, Oid prsid, text *txt) + st->cur = 0; + + funcctx->user_fctx = (void *) st; +- tupdesc = CreateTemplateTupleDesc(2, false); ++ tupdesc = CreateTemplateTupleDesc(2, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "token", +diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c +index faad11e..53e6944 100644 +--- a/src/backend/utils/adt/acl.c ++++ b/src/backend/utils/adt/acl.c +@@ -1695,7 +1695,7 @@ aclexplode(PG_FUNCTION_ARGS) + * build tupdesc for result tuples (matches out parameters in pg_proc + * entry) + */ +- tupdesc = CreateTemplateTupleDesc(4, false); ++ tupdesc = CreateTemplateTupleDesc(4, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "grantor", + OIDOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee", +diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c +index ed81ced..f3d4b99 100644 +--- a/src/backend/utils/adt/datetime.c ++++ b/src/backend/utils/adt/datetime.c +@@ -4193,7 +4193,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS) + * build tupdesc for result tuples. This must match this function's + * pg_proc entry! + */ +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "abbrev", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "utc_offset", +@@ -4286,7 +4286,7 @@ pg_timezone_names(PG_FUNCTION_ARGS) + * build tupdesc for result tuples. This must match this function's + * pg_proc entry! + */ +- tupdesc = CreateTemplateTupleDesc(4, false); ++ tupdesc = CreateTemplateTupleDesc(4, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "abbrev", +diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c +index a4e0252..36d07b7 100644 +--- a/src/backend/utils/adt/dbsize.c ++++ b/src/backend/utils/adt/dbsize.c +@@ -21,6 +21,7 @@ + #include "commands/dbcommands.h" + #include "commands/tablespace.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -90,6 +91,9 @@ calculate_database_size(Oid dbOid) + aclcheck_error(aclresult, ACL_KIND_DATABASE, + get_database_name(dbOid)); + ++ /* SELinux checks */ ++ sepgsql_database_getattr(dbOid); ++ + /* Shared storage in pg_global is not counted */ + + /* Include pg_default storage */ +@@ -178,6 +182,8 @@ calculate_tablespace_size(Oid tblspcOid) + aclcheck_error(aclresult, ACL_KIND_TABLESPACE, + get_tablespace_name(tblspcOid)); + } ++ /* SELinux checks */ ++ sepgsql_tablespace_getattr(tblspcOid); + + if (tblspcOid == DEFAULTTABLESPACE_OID) + snprintf(tblspcPath, MAXPGPATH, "base"); +diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c +index e074b79..0b58f1c 100644 +--- a/src/backend/utils/adt/genfile.c ++++ b/src/backend/utils/adt/genfile.c +@@ -173,7 +173,7 @@ pg_stat_file(PG_FUNCTION_ARGS) + * This record type had better match the output parameters declared for me + * in pg_proc.h. + */ +- tupdesc = CreateTemplateTupleDesc(6, false); ++ tupdesc = CreateTemplateTupleDesc(6, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, + "size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, +diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c +index 07e6aab..e5c8182 100644 +--- a/src/backend/utils/adt/lockfuncs.c ++++ b/src/backend/utils/adt/lockfuncs.c +@@ -85,7 +85,7 @@ pg_lock_status(PG_FUNCTION_ARGS) + + /* build tupdesc for result tuples */ + /* this had better match pg_locks view in system_views.sql */ +- tupdesc = CreateTemplateTupleDesc(14, false); ++ tupdesc = CreateTemplateTupleDesc(14, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database", +diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c +index 11342b2..0ddf907 100644 +--- a/src/backend/utils/adt/misc.c ++++ b/src/backend/utils/adt/misc.c +@@ -322,7 +322,7 @@ pg_get_keywords(PG_FUNCTION_ARGS) + funcctx = SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catcode", +diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c +index 8379407..d44655b 100644 +--- a/src/backend/utils/adt/pgstatfuncs.c ++++ b/src/backend/utils/adt/pgstatfuncs.c +@@ -419,7 +419,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + +- tupdesc = CreateTemplateTupleDesc(11, false); ++ tupdesc = CreateTemplateTupleDesc(11, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0); +diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c +index 9db070d..9df61c6 100644 +--- a/src/backend/utils/adt/ri_triggers.c ++++ b/src/backend/utils/adt/ri_triggers.c +@@ -30,6 +30,7 @@ + + #include "postgres.h" + ++#include "access/sysattr.h" + #include "access/xact.h" + #include "catalog/pg_constraint.h" + #include "catalog/pg_operator.h" +@@ -39,6 +40,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_relation.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -2624,6 +2626,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) + char fkrelname[MAX_QUOTED_REL_NAME_LEN]; + char pkattname[MAX_QUOTED_NAME_LEN + 3]; + char fkattname[MAX_QUOTED_NAME_LEN + 3]; ++ Bitmapset *pkColumns = NULL; ++ Bitmapset *fkColumns = NULL; + const char *sep; + int i; + int old_work_mem; +@@ -2645,6 +2649,18 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) + + ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false); + ++ for (i = 0; i < riinfo.nkeys; i++) ++ { ++ fkColumns = bms_add_member(fkColumns, riinfo.fk_attnums[i] ++ - FirstLowInvalidHeapAttributeNumber); ++ pkColumns = bms_add_member(pkColumns, riinfo.pk_attnums[i] ++ - FirstLowInvalidHeapAttributeNumber); ++ } ++ ++ if (!sepgsql_relation_perms(RelationGetRelid(pk_rel), ++ ACL_SELECT, pkColumns, NULL, false)) ++ return false; ++ + /*---------- + * The query string built is: + * SELECT fk.keycols FROM ONLY relname fk +@@ -3204,6 +3220,7 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, + Relation query_rel; + Oid save_userid; + int save_sec_context; ++ int save_rowlv_mode; + + /* + * The query is always run against the FK table except when this is an +@@ -3220,6 +3237,12 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, + GetUserIdAndSecContext(&save_userid, &save_sec_context); + SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner, + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); ++ /* ++ * When we scan FK relation, switch row-level access control mode ++ * into abort-on-violation mode, to keep referencial integrity. ++ */ ++ if (query_rel == fk_rel) ++ save_rowlv_mode = sepgsql_rowlv_set_mode(SEPGSQL_ROWLV_ABORT); + + /* Create the plan */ + qplan = SPI_prepare(querystr, nargs, argtypes); +@@ -3229,6 +3252,8 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, + + /* Restore UID and security context */ + SetUserIdAndSecContext(save_userid, save_sec_context); ++ if (query_rel == fk_rel) ++ sepgsql_rowlv_set_mode(save_rowlv_mode); + + /* Save the plan if requested */ + if (cache_plan) +diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c +index c837e67..1bf4eb8 100644 +--- a/src/backend/utils/adt/tid.c ++++ b/src/backend/utils/adt/tid.c +@@ -27,6 +27,7 @@ + #include "libpq/pqformat.h" + #include "miscadmin.h" + #include "parser/parsetree.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/rel.h" +@@ -347,6 +348,8 @@ currtid_byreloid(PG_FUNCTION_ARGS) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_relation_getattr(RelationGetRelid(rel)); + + if (rel->rd_rel->relkind == RELKIND_VIEW) + return currtid_for_view(rel, tid); +@@ -377,6 +380,8 @@ currtid_byrelname(PG_FUNCTION_ARGS) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_relation_getattr(RelationGetRelid(rel)); + + if (rel->rd_rel->relkind == RELKIND_VIEW) + return currtid_for_view(rel, tid); +diff --git a/src/backend/utils/adt/trigfuncs.c b/src/backend/utils/adt/trigfuncs.c +index 70246fb..b996537 100644 +--- a/src/backend/utils/adt/trigfuncs.c ++++ b/src/backend/utils/adt/trigfuncs.c +@@ -76,6 +76,10 @@ suppress_redundant_updates_trigger(PG_FUNCTION_ARGS) + !OidIsValid(HeapTupleHeaderGetOid(newheader))) + HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader)); + ++ if (trigdata->tg_relation->rd_rel->relhassecids && ++ !OidIsValid(HeapTupleHeaderGetSecid(newheader))) ++ HeapTupleHeaderSetSecid(newheader, HeapTupleHeaderGetSecid(oldheader)); ++ + /* if the tuple payload is the same ... */ + if (newtuple->t_len == oldtuple->t_len && + newheader->t_hoff == oldheader->t_hoff && +diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c +index 78f08f4..4a44810 100644 +--- a/src/backend/utils/adt/tsvector_op.c ++++ b/src/backend/utils/adt/tsvector_op.c +@@ -975,7 +975,7 @@ ts_setup_firstcall(FunctionCallInfo fcinfo, FuncCallContext *funcctx, + } + Assert(stat->stackpos <= stat->maxdepth); + +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "ndoc", +diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c +index 88d8d8b..281acb0 100644 +--- a/src/backend/utils/cache/plancache.c ++++ b/src/backend/utils/cache/plancache.c +@@ -924,12 +924,12 @@ PlanCacheComputeResultDesc(List *stmt_list) + if (IsA(node, Query)) + { + query = (Query *) node; +- return ExecCleanTypeFromTL(query->targetList, false); ++ return ExecCleanTypeFromTL(query->targetList, false, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; +- return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); ++ return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false); + } + /* other cases shouldn't happen, but return NULL */ + break; +@@ -940,13 +940,13 @@ PlanCacheComputeResultDesc(List *stmt_list) + { + query = (Query *) node; + Assert(query->returningList); +- return ExecCleanTypeFromTL(query->returningList, false); ++ return ExecCleanTypeFromTL(query->returningList, false, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; + Assert(pstmt->hasReturning); +- return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); ++ return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false); + } + /* other cases shouldn't happen, but return NULL */ + break; +diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c +index 073d25a..249b4c0 100644 +--- a/src/backend/utils/cache/relcache.c ++++ b/src/backend/utils/cache/relcache.c +@@ -213,7 +213,7 @@ static void write_relcache_init_file(bool shared); + static void write_item(const void *data, Size len, FILE *fp); + + static void formrdesc(const char *relationName, Oid relationReltype, +- bool isshared, bool hasoids, ++ bool isshared, bool hasoids, bool hassecids, + int natts, const FormData_pg_attribute *attrs); + + static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK); +@@ -352,7 +352,8 @@ AllocateRelationDesc(Form_pg_class relp) + + /* and allocate attribute tuple form storage */ + relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, +- relationForm->relhasoids); ++ relationForm->relhasoids, ++ relationForm->relhassecids); + /* which we mark as a reference-counted tupdesc */ + relation->rd_att->tdrefcount = 1; + +@@ -434,6 +435,7 @@ RelationBuildTupleDesc(Relation relation) + relation->rd_att->tdtypeid = relation->rd_rel->reltype; + relation->rd_att->tdtypmod = -1; /* unnecessary, but... */ + relation->rd_att->tdhasoid = relation->rd_rel->relhasoids; ++ relation->rd_att->tdhassecid = relation->rd_rel->relhassecids; + + constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext, + sizeof(TupleConstr)); +@@ -1392,7 +1394,7 @@ LookupOpclassInfo(Oid operatorClassOid, + */ + static void + formrdesc(const char *relationName, Oid relationReltype, +- bool isshared, bool hasoids, ++ bool isshared, bool hasoids, bool hassecids, + int natts, const FormData_pg_attribute *attrs) + { + Relation relation; +@@ -1455,6 +1457,7 @@ formrdesc(const char *relationName, Oid relationReltype, + relation->rd_rel->reltuples = 1; + relation->rd_rel->relkind = RELKIND_RELATION; + relation->rd_rel->relhasoids = hasoids; ++ relation->rd_rel->relhassecids = hassecids; + relation->rd_rel->relnatts = (int16) natts; + + /* +@@ -1464,7 +1467,7 @@ formrdesc(const char *relationName, Oid relationReltype, + * because it will never be replaced. The input values must be correctly + * defined by macros in src/include/catalog/ headers. + */ +- relation->rd_att = CreateTemplateTupleDesc(natts, hasoids); ++ relation->rd_att = CreateTemplateTupleDesc(natts, hasoids, hassecids); + relation->rd_att->tdrefcount = 1; /* mark as refcounted */ + + relation->rd_att->tdtypeid = relationReltype; +@@ -2527,6 +2530,7 @@ RelationBuildLocalRelation(const char *relname, + + rel->rd_rel->relkind = RELKIND_UNCATALOGED; + rel->rd_rel->relhasoids = rel->rd_att->tdhasoid; ++ rel->rd_rel->relhassecids = rel->rd_att->tdhassecid; + rel->rd_rel->relnatts = natts; + rel->rd_rel->reltype = InvalidOid; + /* needed when bootstrapping: */ +@@ -2767,7 +2771,7 @@ RelationCacheInitializePhase2(void) + if (!load_relcache_init_file(true)) + { + formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true, +- true, Natts_pg_database, Desc_pg_database); ++ true, true, Natts_pg_database, Desc_pg_database); + + #define NUM_CRITICAL_SHARED_RELS 1 /* fix if you change list above */ + } +@@ -2818,13 +2822,13 @@ RelationCacheInitializePhase3(void) + needNewCacheFile = true; + + formrdesc("pg_class", RelationRelation_Rowtype_Id, false, +- true, Natts_pg_class, Desc_pg_class); ++ true, true, Natts_pg_class, Desc_pg_class); + formrdesc("pg_attribute", AttributeRelation_Rowtype_Id, false, +- false, Natts_pg_attribute, Desc_pg_attribute); ++ false, true, Natts_pg_attribute, Desc_pg_attribute); + formrdesc("pg_proc", ProcedureRelation_Rowtype_Id, false, +- true, Natts_pg_proc, Desc_pg_proc); ++ true, true, Natts_pg_proc, Desc_pg_proc); + formrdesc("pg_type", TypeRelation_Rowtype_Id, false, +- true, Natts_pg_type, Desc_pg_type); ++ true, true, Natts_pg_type, Desc_pg_type); + + #define NUM_CRITICAL_LOCAL_RELS 4 /* fix if you change list above */ + } +@@ -2969,6 +2973,7 @@ RelationCacheInitializePhase3(void) + Assert(relation->rd_att->tdtypeid == relp->reltype); + Assert(relation->rd_att->tdtypmod == -1); + Assert(relation->rd_att->tdhasoid == relp->relhasoids); ++ Assert(relation->rd_att->tdhassecid == relp->relhassecids); + + ReleaseSysCache(htup); + +@@ -3079,7 +3084,7 @@ load_critical_index(Oid indexoid, Oid heapoid) + */ + static TupleDesc + BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, +- bool hasoids) ++ bool hasoids, bool hassecids) + { + TupleDesc result; + MemoryContext oldcxt; +@@ -3087,7 +3092,7 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, + + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + +- result = CreateTemplateTupleDesc(natts, hasoids); ++ result = CreateTemplateTupleDesc(natts, hasoids, hassecids); + result->tdtypeid = RECORDOID; /* not right, but we don't care */ + result->tdtypmod = -1; + +@@ -3117,7 +3122,7 @@ GetPgClassDescriptor(void) + if (pgclassdesc == NULL) + pgclassdesc = BuildHardcodedDescriptor(Natts_pg_class, + Desc_pg_class, +- true); ++ true, true); + + return pgclassdesc; + } +@@ -3131,7 +3136,7 @@ GetPgIndexDescriptor(void) + if (pgindexdesc == NULL) + pgindexdesc = BuildHardcodedDescriptor(Natts_pg_index, + Desc_pg_index, +- false); ++ false, false); + + return pgindexdesc; + } +@@ -3947,7 +3952,8 @@ load_relcache_init_file(bool shared) + + /* initialize attribute tuple forms */ + rel->rd_att = CreateTemplateTupleDesc(relform->relnatts, +- relform->relhasoids); ++ relform->relhasoids, ++ relform->relhassecids); + rel->rd_att->tdrefcount = 1; /* mark as refcounted */ + + rel->rd_att->tdtypeid = relform->reltype; +diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c +index 61b06ac..b08654b 100644 +--- a/src/backend/utils/cache/syscache.c ++++ b/src/backend/utils/cache/syscache.c +@@ -895,6 +895,30 @@ GetSysCacheOid(int cacheId, + return result; + } + ++/* ++ * GetSysCacheSecid ++ * ++ * A convenience routine that does SearchSysCache and returns the ++ * security-id of the found tuple, or InvalidOid if no tuple could ++ * be found. No lock is retained on the syscache entry. ++ */ ++Oid ++GetSysCacheSecid(int cacheId, ++ Datum key1, ++ Datum key2, ++ Datum key3, ++ Datum key4) ++{ ++ HeapTuple tuple; ++ Oid result; ++ ++ tuple = SearchSysCache(cacheId, key1, key2, key3, key4); ++ if (!HeapTupleIsValid(tuple)) ++ return InvalidOid; ++ result = HeapTupleGetSecid(tuple); ++ ReleaseSysCache(tuple); ++ return result; ++} + + /* + * SearchSysCacheAttName +diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c +index c3c0440..fb5b762 100644 +--- a/src/backend/utils/fmgr/fmgr.c ++++ b/src/backend/utils/fmgr/fmgr.c +@@ -24,6 +24,7 @@ + #include "miscadmin.h" + #include "nodes/nodeFuncs.h" + #include "pgstat.h" ++#include "sepgsql/hooks.h" + #include "utils/builtins.h" + #include "utils/fmgrtab.h" + #include "utils/guc.h" +@@ -190,6 +191,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, + finfo->fn_extra = NULL; + finfo->fn_mcxt = mcxt; + finfo->fn_expr = NULL; /* caller may set this later */ ++ finfo->fn_seclabel = NULL; + + if ((fbp = fmgr_isbuiltin(functionId)) != NULL) + { +@@ -228,15 +230,22 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, + * ability to set the track_functions GUC as a local GUC parameter of an + * interesting function and have the right things happen. + */ +- if (!ignore_security && +- (procedureStruct->prosecdef || +- !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig))) ++ if (!ignore_security) + { +- finfo->fn_addr = fmgr_security_definer; +- finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ +- finfo->fn_oid = functionId; +- ReleaseSysCache(procedureTuple); +- return; ++ char *seclabel ++ = sepgsql_proc_domtrans(procedureTuple, mcxt); ++ ++ if (procedureStruct->prosecdef || ++ !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig) || ++ seclabel != NULL) ++ { ++ finfo->fn_addr = fmgr_security_definer; ++ finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ ++ finfo->fn_oid = functionId; ++ finfo->fn_seclabel = seclabel; ++ ReleaseSysCache(procedureTuple); ++ return; ++ } + } + + switch (procedureStruct->prolang) +@@ -877,6 +886,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS) + FmgrInfo *save_flinfo; + Oid save_userid; + int save_sec_context; ++ char *save_seclabel; + volatile int save_nestlevel; + PgStat_FunctionCallUsage fcusage; + +@@ -939,6 +949,10 @@ fmgr_security_definer(PG_FUNCTION_ARGS) + PGC_S_SESSION, + GUC_ACTION_SAVE); + } ++ if (fcinfo->flinfo->fn_seclabel) ++ save_seclabel = sepgsql_set_client_label(fcinfo->flinfo->fn_seclabel); ++ else ++ save_seclabel = NULL; + + /* + * We don't need to restore GUC or userid settings on error, because the +@@ -978,6 +992,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS) + AtEOXact_GUC(true, save_nestlevel); + if (OidIsValid(fcache->userid)) + SetUserIdAndSecContext(save_userid, save_sec_context); ++ if (fcinfo->flinfo->fn_seclabel) ++ sepgsql_set_client_label(save_seclabel); + + return result; + } +diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c +index 382008c..e6eba47 100644 +--- a/src/backend/utils/fmgr/funcapi.c ++++ b/src/backend/utils/fmgr/funcapi.c +@@ -1105,7 +1105,7 @@ build_function_result_tupdesc_d(Datum proallargtypes, + if (numoutargs < 2) + return NULL; + +- desc = CreateTemplateTupleDesc(numoutargs, false); ++ desc = CreateTemplateTupleDesc(numoutargs, false, false); + for (i = 0; i < numoutargs; i++) + { + TupleDescInitEntry(desc, i + 1, +@@ -1220,7 +1220,7 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases) + /* OK, get the column alias */ + attname = strVal(linitial(colaliases)); + +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, + (AttrNumber) 1, + attname, +diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c +index ed99b91..2bc5127 100644 +--- a/src/backend/utils/init/postinit.c ++++ b/src/backend/utils/init/postinit.c +@@ -37,6 +37,7 @@ + #include "postmaster/autovacuum.h" + #include "postmaster/postmaster.h" + #include "replication/walsender.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "storage/ipc.h" +@@ -304,6 +305,9 @@ CheckMyDatabase(const char *name, bool am_superuser) + errmsg("permission denied for database \"%s\"", name), + errdetail("User does not have CONNECT privilege."))); + ++ /* SELinux checks */ ++ sepgsql_database_connect(MyDatabaseId); ++ + /* + * Check connection limit for this database. + * +@@ -735,6 +739,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, + /* set up ACL framework (so CheckMyDatabase can check permissions) */ + initialize_acl(); + ++ /* set up enhanced security feature */ ++ sepgsql_initialize(); ++ + /* Process pg_db_role_setting options */ + process_settings(MyDatabaseId, GetSessionUserId()); + +diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c +index f198c9c..8a8d45d 100644 +--- a/src/backend/utils/misc/guc.c ++++ b/src/backend/utils/misc/guc.c +@@ -31,6 +31,7 @@ + #include "access/twophase.h" + #include "access/xact.h" + #include "catalog/namespace.h" ++#include "catalog/pg_seclabel.h" + #include "commands/async.h" + #include "commands/prepare.h" + #include "commands/vacuum.h" +@@ -56,6 +57,7 @@ + #include "postmaster/syslogger.h" + #include "postmaster/walwriter.h" + #include "replication/walsender.h" ++#include "sepgsql/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "tcop/tcopprot.h" +@@ -337,6 +339,18 @@ static const struct config_enum_entry constraint_exclusion_options[] = { + {NULL, 0, false} + }; + ++#ifdef HAVE_SELINUX ++static const struct config_enum_entry sepostgresql_options[] = { ++ {"default", SEPGSQL_MODE_DEFAULT, false}, ++ {"enforcing", SEPGSQL_MODE_ENFORCING, false}, ++ {"permissive", SEPGSQL_MODE_PERMISSIVE, false}, ++ {"disabled", SEPGSQL_MODE_DISABLED, false}, ++ {"on", SEPGSQL_MODE_DEFAULT, true}, ++ {"off", SEPGSQL_MODE_DISABLED, true}, ++ {NULL, 0, false}, ++}; ++#endif ++ + /* + * Options for enum values stored in other modules + */ +@@ -365,6 +379,7 @@ bool log_btree_build_stats = false; + + bool check_function_bodies = true; + bool default_with_oids = false; ++bool default_with_secids = true; + bool SQL_inheritance = true; + + bool Password_encryption = true; +@@ -1085,6 +1100,14 @@ static struct config_bool ConfigureNamesBool[] = + false, NULL, NULL + }, + { ++ {"default_with_secids", PGC_USERSET, CONN_AUTH_SETTINGS, ++ gettext_noop("Create new tables with security-ids by default."), ++ NULL ++ }, ++ &default_with_secids, ++ true, NULL, NULL ++ }, ++ { + {"logging_collector", PGC_POSTMASTER, LOGGING_WHERE, + gettext_noop("Start a subprocess to capture stderr output and/or csvlogs into log files."), + NULL +@@ -1254,6 +1277,36 @@ static struct config_bool ConfigureNamesBool[] = + false, NULL, NULL + }, + ++ { ++ {"ignore_security_label_input", PGC_USERSET, CONN_AUTH_SECURITY, ++ gettext_noop("Disables to assign used provided security label"), ++ NULL, ++ GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE ++ }, ++ &ignore_security_label_input, ++ false, NULL, NULL ++ }, ++#ifdef HAVE_SELINUX ++ { ++ {"sepostgresql_mcstrans", PGC_USERSET, CONN_AUTH_SECURITY, ++ gettext_noop("Enables to show security context in human-readable form"), ++ NULL, ++ GUC_NOT_IN_SAMPLE ++ }, ++ &sepgsql_mcstrans, ++ true, NULL, NULL ++ }, ++ { ++ {"sepostgresql_debug_audit", PGC_USERSET, CONN_AUTH_SECURITY, ++ gettext_noop("Enables to show audit logs for debugging"), ++ NULL, ++ GUC_NOT_IN_SAMPLE ++ }, ++ &sepgsql_debug_audit, ++ false, NULL, NULL ++ }, ++#endif ++ + /* End-of-list marker */ + { + {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL +@@ -2795,7 +2848,16 @@ static struct config_enum ConfigureNamesEnum[] = + &xmloption, + XMLOPTION_CONTENT, xmloption_options, NULL, NULL + }, +- ++#ifdef HAVE_SELINUX ++ { ++ {"sepostgresql", PGC_POSTMASTER, CONN_AUTH_SECURITY, ++ gettext_noop("Enables to set SE-PostgreSQL's mode"), ++ NULL ++ }, ++ &sepostgresql_mode, ++ SEPGSQL_MODE_DEFAULT, sepostgresql_options, NULL, sepgsql_show_mode ++ }, ++#endif + + /* End-of-list marker */ + { +@@ -6070,7 +6132,7 @@ GetPGVariableResultDesc(const char *name) + if (guc_name_compare(name, "all") == 0) + { + /* need a tuple descriptor representing three TEXT columns */ +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", +@@ -6086,7 +6148,7 @@ GetPGVariableResultDesc(const char *name) + (void) GetConfigOptionByName(name, &varname); + + /* need a tuple descriptor representing a single TEXT column */ +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, + TEXTOID, -1, 0); + } +@@ -6109,7 +6171,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest) + value = GetConfigOptionByName(name, &varname); + + /* need a tuple descriptor representing a single TEXT column */ +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, + TEXTOID, -1, 0); + +@@ -6136,7 +6198,7 @@ ShowAllGUCConfig(DestReceiver *dest) + bool isnull[3] = {false, false, false}; + + /* need a tuple descriptor representing three TEXT columns */ +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", +@@ -6531,7 +6593,7 @@ show_all_settings(PG_FUNCTION_ARGS) + * need a tuple descriptor representing NUM_PG_SETTINGS_ATTS columns + * of the appropriate types + */ +- tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false); ++ tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", +diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample +index 02f1df0..f8a27c8 100644 +--- a/src/backend/utils/misc/postgresql.conf.sample ++++ b/src/backend/utils/misc/postgresql.conf.sample +@@ -76,6 +76,8 @@ + + # - Security and Authentication - + ++#sepostgresql = disabled # default|enforcing|permissive|disabled ++ + #authentication_timeout = 1min # 1s-600s + #ssl = off # (change requires restart) + #ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH' # allowed SSL ciphers +diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c +index ac62d45..f05a094 100644 +--- a/src/backend/utils/mmgr/portalmem.c ++++ b/src/backend/utils/mmgr/portalmem.c +@@ -894,7 +894,7 @@ pg_cursor(PG_FUNCTION_ARGS) + * build tupdesc for result tuples. This must match the definition of the + * pg_cursors view in system_views.sql + */ +- tupdesc = CreateTemplateTupleDesc(6, false); ++ tupdesc = CreateTemplateTupleDesc(6, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", +diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c +index f40ad87..497bdf0 100644 +--- a/src/bin/initdb/initdb.c ++++ b/src/bin/initdb/initdb.c +@@ -87,6 +87,7 @@ static bool debug = false; + static bool noclean = false; + static bool show_setting = false; + static char *xlog_dir = ""; ++static bool enable_selinux = false; + + + /* internal vars */ +@@ -1163,6 +1164,13 @@ setup_config(void) + "#default_text_search_config = 'pg_catalog.simple'", + repltok); + ++ if (enable_selinux) ++ { ++ strcpy(repltok, "sepostgresql = default"); ++ conflines = replace_token(conflines, ++ "#sepostgresql = disabled", repltok); ++ } ++ + snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data); + + writefile(path, conflines); +@@ -2394,6 +2402,7 @@ usage(const char *progname) + printf(_(" -U, --username=NAME database superuser name\n")); + printf(_(" -W, --pwprompt prompt for a password for the new superuser\n")); + printf(_(" -X, --xlogdir=XLOGDIR location for the transaction log directory\n")); ++ printf(_(" --enable-selinux enables SELinux support\n")); + printf(_("\nLess commonly used options:\n")); + printf(_(" -d, --debug generate lots of debugging output\n")); + printf(_(" -L DIRECTORY where to find the input files\n")); +@@ -2436,6 +2445,7 @@ main(int argc, char *argv[]) + {"show", no_argument, NULL, 's'}, + {"noclean", no_argument, NULL, 'n'}, + {"xlogdir", required_argument, NULL, 'X'}, ++ {"enable-selinux", no_argument, NULL, 10}, + {NULL, 0, NULL, 0} + }; + +@@ -2545,6 +2555,9 @@ main(int argc, char *argv[]) + case 9: + pwfilename = xstrdup(optarg); + break; ++ case 10: ++ enable_selinux = true; ++ break; + case 's': + show_setting = true; + break; +diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h +index 14fe652..40b7b40 100644 +--- a/src/bin/pg_dump/pg_backup.h ++++ b/src/bin/pg_dump/pg_backup.h +@@ -103,6 +103,7 @@ typedef struct _restoreOptions + * restore */ + int use_setsessauth;/* Use SET SESSION AUTHORIZATION commands + * instead of OWNER TO */ ++ int noSecLabel; /* Don't try to restore security labels */ + char *superuser; /* Username to use as superuser */ + char *use_role; /* Issue SET ROLE to this */ + int dataOnly; +@@ -167,7 +168,7 @@ extern void ArchiveEntry(Archive *AHX, + CatalogId catalogId, DumpId dumpId, + const char *tag, + const char *namespace, const char *tablespace, +- const char *owner, bool withOids, ++ const char *owner, bool withOids, bool withSecids, + const char *desc, teSection section, + const char *defn, + const char *dropStmt, const char *copyStmt, +diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c +index d83f4dc..cc0cbae 100644 +--- a/src/bin/pg_dump/pg_backup_archiver.c ++++ b/src/bin/pg_dump/pg_backup_archiver.c +@@ -540,6 +540,8 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, + } + else + { ++ bool need_reset = false; ++ + _disableTriggersIfNecessary(AH, te, ropt); + + /* Select owner and schema as necessary */ +@@ -575,6 +577,16 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, + "ONLY " : ""), + fmtId(te->tag)); + } ++ /* ++ * If data section has security_label, but pg_restore works ++ * with --no-security-label, we set server flag to ignore ++ * the security label input. ++ */ ++ if (ropt->noSecLabel && te->withSecids) ++ { ++ ahprintf(AH, "SET ignore_security_label_input = on;\n\n"); ++ need_reset = true; ++ } + + /* + * If we have a copy statement, use it. As of V1.3, these +@@ -595,6 +607,9 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, + + AH->writingCopyData = false; + ++ if (need_reset) ++ ahprintf(AH, "RESET ignore_security_label_input;\n\n"); ++ + /* close out the transaction started above */ + if (is_parallel && te->created) + CommitTransaction(AH); +@@ -712,7 +727,7 @@ ArchiveEntry(Archive *AHX, + const char *tag, + const char *namespace, + const char *tablespace, +- const char *owner, bool withOids, ++ const char *owner, bool withOids, bool withSecids, + const char *desc, teSection section, + const char *defn, + const char *dropStmt, const char *copyStmt, +@@ -744,6 +759,7 @@ ArchiveEntry(Archive *AHX, + newToc->tablespace = tablespace ? strdup(tablespace) : NULL; + newToc->owner = strdup(owner); + newToc->withOids = withOids; ++ newToc->withSecids = withSecids; + newToc->desc = strdup(desc); + newToc->defn = strdup(defn); + newToc->dropStmt = strdup(dropStmt); +@@ -2027,6 +2043,7 @@ WriteToc(ArchiveHandle *AH) + WriteStr(AH, te->tablespace); + WriteStr(AH, te->owner); + WriteStr(AH, te->withOids ? "true" : "false"); ++ WriteStr(AH, te->withSecids ? "true" : "false"); + + /* Dump list of dependencies */ + for (i = 0; i < te->nDeps; i++) +@@ -2138,6 +2155,16 @@ ReadToc(ArchiveHandle *AH) + else + te->withOids = true; + ++ if (AH->version >= K_VERS_1_13) ++ { ++ if (strcmp(ReadStr(AH), "true") == 0) ++ te->withSecids = true; ++ else ++ te->withSecids = false; ++ } ++ else ++ te->withSecids = false; ++ + /* Read TOC entry dependencies */ + if (AH->version >= K_VERS_1_5) + { +@@ -2256,6 +2283,9 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) + if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te)) + return 0; + ++ if (ropt->noSecLabel && strcmp(te->desc, "LABEL") == 0) ++ return 0; ++ + /* Ignore DATABASE entry unless we should create it */ + if (!ropt->create && strcmp(te->desc, "DATABASE") == 0) + return 0; +@@ -2322,6 +2352,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) + (strcmp(te->desc, "ACL") == 0 && + strncmp(te->tag, "LARGE OBJECT ", 13) == 0) || + (strcmp(te->desc, "COMMENT") == 0 && ++ strncmp(te->tag, "LARGE OBJECT ", 13) == 0) || ++ (strcmp(te->desc, "LABEL") == 0 && + strncmp(te->tag, "LARGE OBJECT ", 13) == 0)) + res = res & REQ_DATA; + else +@@ -2473,6 +2505,36 @@ _doSetWithOids(ArchiveHandle *AH, const bool withOids) + destroyPQExpBuffer(cmd); + } + ++/* ++ * Issue a SET default_with_secids command. Caller is responsible ++ * for updating state if appropriate. ++ */ ++static void ++_doSetWithSecids(ArchiveHandle *AH, const bool withSecids) ++{ ++ PQExpBuffer cmd = createPQExpBuffer(); ++ ++ appendPQExpBuffer(cmd, "SET default_with_secids = %s;", withSecids ? ++ "true" : "false"); ++ ++ if (RestoringToDB(AH)) ++ { ++ PGresult *res; ++ ++ res = PQexec(AH->connection, cmd->data); ++ ++ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) ++ warn_or_die_horribly(AH, modulename, ++ "could not set default_with_secids: %s", ++ PQerrorMessage(AH->connection)); ++ ++ PQclear(res); ++ } ++ else ++ ahprintf(AH, "%s\n\n", cmd->data); ++ ++ destroyPQExpBuffer(cmd); ++} + + /* + * Issue the commands to connect to the specified database. +@@ -2571,6 +2633,18 @@ _setWithOids(ArchiveHandle *AH, TocEntry *te) + } + } + ++/* ++ * Set the proper default_with_secids value for the table. ++ */ ++static void ++_setWithSecids(ArchiveHandle *AH, TocEntry *te) ++{ ++ if (AH->currWithSecids != te->withSecids) ++ { ++ _doSetWithSecids(AH, te->withSecids); ++ AH->currWithSecids = te->withSecids; ++ } ++} + + /* + * Issue the commands to select the specified schema as the current schema +@@ -2808,9 +2882,12 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat + _selectOutputSchema(AH, te->namespace); + _selectTablespace(AH, te->tablespace); + +- /* Set up OID mode too */ ++ /* Set up OID/SECID mode too */ + if (strcmp(te->desc, "TABLE") == 0) ++ { + _setWithOids(AH, te); ++ _setWithSecids(AH, te); ++ } + + /* Emit header comment for item */ + if (!AH->noTocComments) +diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h +index 2e944c1..67f62ac 100644 +--- a/src/bin/pg_dump/pg_backup_archiver.h ++++ b/src/bin/pg_dump/pg_backup_archiver.h +@@ -63,7 +63,7 @@ typedef z_stream *z_streamp; + + /* Current archive version number (the format we can output) */ + #define K_VERS_MAJOR 1 +-#define K_VERS_MINOR 12 ++#define K_VERS_MINOR 13 + #define K_VERS_REV 0 + + /* Data block types */ +@@ -89,9 +89,10 @@ typedef z_stream *z_streamp; + * indicator */ + #define K_VERS_1_12 (( (1 * 256 + 12) * 256 + 0) * 256 + 0) /* add separate BLOB + * entries */ +- ++#define K_VERS_1_13 (( (1 * 256 + 13) * 256 + 0) * 256 + 0) /* add security label ++ * support */ + /* Newest format we can read */ +-#define K_VERS_MAX (( (1 * 256 + 12) * 256 + 255) * 256 + 0) ++#define K_VERS_MAX (( (1 * 256 + 13) * 256 + 255) * 256 + 0) + + + /* Flags to indicate disposition of offsets stored in files */ +@@ -278,6 +279,7 @@ typedef struct _archiveHandle + char *currSchema; /* current schema, or NULL */ + char *currTablespace; /* current tablespace, or NULL */ + bool currWithOids; /* current default_with_oids setting */ ++ bool currWithSecids; /* current default_with_secids setting */ + + void *lo_buf; + size_t lo_buf_used; +@@ -305,6 +307,7 @@ typedef struct _tocEntry + * means use database default */ + char *owner; + bool withOids; /* Used only by "TABLE" tags */ ++ bool withSecids; /* Used only by "TABLE" tags */ + char *desc; + char *defn; + char *dropStmt; +diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c +index 2431d71..e7ef296 100644 +--- a/src/bin/pg_dump/pg_dump.c ++++ b/src/bin/pg_dump/pg_dump.c +@@ -125,7 +125,7 @@ static int binary_upgrade = 0; + static int disable_dollar_quoting = 0; + static int dump_inserts = 0; + static int column_inserts = 0; +- ++static int security_label = 0; + + static void help(const char *progname); + static void expand_schema_name_patterns(SimpleStringList *patterns, +@@ -183,6 +183,11 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, + const char *tag, const char *nspname, const char *owner, + const char *acls); + ++static void dumpSecLabel(Archive *fout, DumpId objDumpId, ++ const char *target, ++ const char *namespace, ++ const char *seclabel); ++ + static void getDependencies(void); + static void getDomainConstraints(TypeInfo *tyinfo); + static void getTableData(TableInfo *tblinfo, int numTables, bool oids); +@@ -215,7 +220,7 @@ static bool binary_upgrade_set_type_oids_by_rel_oid( + static void binary_upgrade_set_relfilenodes(PQExpBuffer upgrade_buffer, + Oid pg_class_oid, bool is_index); + static const char *getAttrName(int attrnum, TableInfo *tblInfo); +-static const char *fmtCopyColumnList(const TableInfo *ti); ++static const char *fmtCopyColumnList(const TableInfo *ti, bool secids); + static void do_sql_command(PGconn *conn, const char *query); + static void check_sql_result(PGresult *res, PGconn *conn, const char *query, + ExecStatusType expected); +@@ -299,6 +304,7 @@ main(int argc, char **argv) + {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, + {"role", required_argument, NULL, 3}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"security-label", no_argument, &security_label, 1}, + + {NULL, 0, NULL, 0} + }; +@@ -447,6 +453,8 @@ main(int argc, char **argv) + outputNoTablespaces = 1; + else if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; ++ else if (strcmp(optarg, "security-label") == 0) ++ security_label = 1; + else + { + fprintf(stderr, +@@ -515,6 +523,10 @@ main(int argc, char **argv) + exit(1); + } + ++ /* Force column insertion mode, when --security-label mode is given. */ ++ if (security_label && dump_inserts) ++ column_inserts = 1; ++ + /* open the output file */ + if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0) + { +@@ -833,6 +845,7 @@ help(const char *progname) + printf(_(" --use-set-session-authorization\n" + " use SET SESSION AUTHORIZATION commands instead of\n" + " ALTER OWNER commands to set ownership\n")); ++ printf(_(" --security-label dump schema/data with security label\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); +@@ -1127,7 +1140,7 @@ dumpTableData_copy(Archive *fout, void *dcontext) + * cases involving ADD COLUMN and inheritance.) + */ + if (g_fout->remoteVersion >= 70300) +- column_list = fmtCopyColumnList(tbinfo); ++ column_list = fmtCopyColumnList(tbinfo, tdinfo->secids); + else + column_list = ""; /* can't select columns in COPY */ + +@@ -1251,14 +1264,16 @@ dumpTableData_insert(Archive *fout, void *dcontext) + if (fout->remoteVersion >= 70100) + { + appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " +- "SELECT * FROM ONLY %s", ++ "SELECT %s* FROM ONLY %s", ++ (tdinfo->secids ? "security_label," : ""), + fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, + classname)); + } + else + { + appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " +- "SELECT * FROM %s", ++ "SELECT %s* FROM %s", ++ (tdinfo->secids ? "security_label," : ""), + fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, + classname)); + } +@@ -1398,7 +1413,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) + appendPQExpBuffer(copyBuf, "COPY %s ", + fmtId(tbinfo->dobj.name)); + appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n", +- fmtCopyColumnList(tbinfo), ++ fmtCopyColumnList(tbinfo, tdinfo->secids), + (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : ""); + copyStmt = copyBuf->data; + } +@@ -1412,7 +1427,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) + ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId, + tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, + NULL, tbinfo->rolname, +- false, "TABLE DATA", SECTION_DATA, ++ false, tdinfo->secids, "TABLE DATA", SECTION_DATA, + "", "", copyStmt, + tdinfo->dobj.dependencies, tdinfo->dobj.nDeps, + dumpFn, tdinfo); +@@ -1457,6 +1472,7 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids) + tdinfo->dobj.namespace = tblinfo[i].dobj.namespace; + tdinfo->tdtable = &(tblinfo[i]); + tdinfo->oids = oids; ++ tdinfo->secids = (security_label && tblinfo[i].hassecids) ? true : false; + addObjectDependency(&tdinfo->dobj, tblinfo[i].dobj.dumpId); + + tblinfo[i].dataObj = tdinfo; +@@ -1780,6 +1796,7 @@ dumpDatabase(Archive *AH) + NULL, /* Tablespace */ + dba, /* Owner */ + false, /* with oids */ ++ false, /* with secids */ + "DATABASE", /* Desc */ + SECTION_PRE_DATA, /* Section */ + creaQry->data, /* Create */ +@@ -1825,7 +1842,7 @@ dumpDatabase(Archive *AH) + LargeObjectRelationId); + ArchiveEntry(AH, nilCatalogId, createDumpId(), + "pg_largeobject", NULL, NULL, "", +- false, "pg_largeobject", SECTION_PRE_DATA, ++ false, false, "pg_largeobject", SECTION_PRE_DATA, + loOutQry->data, "", NULL, + NULL, 0, + NULL, NULL); +@@ -1857,7 +1874,7 @@ dumpDatabase(Archive *AH) + appendPQExpBuffer(dbQry, ";\n"); + + ArchiveEntry(AH, dbCatId, createDumpId(), datname, NULL, NULL, +- dba, false, "COMMENT", SECTION_NONE, ++ dba, false, false, "COMMENT", SECTION_NONE, + dbQry->data, "", NULL, + &dbDumpId, 1, NULL, NULL); + } +@@ -1896,7 +1913,7 @@ dumpEncoding(Archive *AH) + + ArchiveEntry(AH, nilCatalogId, createDumpId(), + "ENCODING", NULL, NULL, "", +- false, "ENCODING", SECTION_PRE_DATA, ++ false, false, "ENCODING", SECTION_PRE_DATA, + qry->data, "", NULL, + NULL, 0, + NULL, NULL); +@@ -1923,7 +1940,7 @@ dumpStdStrings(Archive *AH) + + ArchiveEntry(AH, nilCatalogId, createDumpId(), + "STDSTRINGS", NULL, NULL, "", +- false, "STDSTRINGS", SECTION_PRE_DATA, ++ false, false, "STDSTRINGS", SECTION_PRE_DATA, + qry->data, "", NULL, + NULL, 0, + NULL, NULL); +@@ -1956,16 +1973,17 @@ getBlobs(Archive *AH) + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ + if (AH->remoteVersion >= 90000) + appendPQExpBuffer(blobQry, +- "SELECT oid, (%s lomowner) AS rolname, lomacl" ++ "SELECT oid, (%s lomowner) AS rolname, lomacl, %s" + " FROM pg_largeobject_metadata", +- username_subquery); ++ username_subquery, ++ security_label ? "security_label" : "NULL"); + else if (AH->remoteVersion >= 70100) + appendPQExpBuffer(blobQry, +- "SELECT DISTINCT loid, NULL::oid, NULL::oid" ++ "SELECT DISTINCT loid, NULL::oid, NULL::oid, NULL::text" + " FROM pg_largeobject"); + else + appendPQExpBuffer(blobQry, +- "SELECT oid, NULL::oid, NULL::oid" ++ "SELECT oid, NULL::oid, NULL::oid, NULL::text" + " FROM pg_class WHERE relkind = 'l'"); + + res = PQexec(g_conn, blobQry->data); +@@ -1995,6 +2013,11 @@ getBlobs(Archive *AH) + binfo[i].blobacl = strdup(PQgetvalue(res, i, 2)); + else + binfo[i].blobacl = NULL; ++ ++ if (!PQgetisnull(res, i, 3)) ++ binfo[i].seclabel = strdup(PQgetvalue(res, i, 3)); ++ else ++ binfo[i].seclabel = NULL; + } + + /* +@@ -2034,7 +2057,7 @@ dumpBlob(Archive *AH, BlobInfo *binfo) + ArchiveEntry(AH, binfo->dobj.catId, binfo->dobj.dumpId, + binfo->dobj.name, + NULL, NULL, +- binfo->rolname, false, ++ binfo->rolname, false, false, + "BLOB", SECTION_PRE_DATA, + cquery->data, dquery->data, NULL, + binfo->dobj.dependencies, binfo->dobj.nDeps, +@@ -2049,6 +2072,10 @@ dumpBlob(Archive *AH, BlobInfo *binfo) + NULL, binfo->rolname, + binfo->dobj.catId, 0, binfo->dobj.dumpId); + ++ /* Dump security label if any */ ++ dumpSecLabel(AH, binfo->dobj.dumpId, ++ cquery->data, NULL, binfo->seclabel); ++ + /* Dump ACL if any */ + if (binfo->blobacl) + dumpACL(AH, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT", +@@ -2356,6 +2383,7 @@ getNamespaces(int *numNamespaces) + int i_nspname; + int i_rolname; + int i_nspacl; ++ int i_seclabel; + + /* + * Before 7.3, there are no real namespaces; create two dummy entries, one +@@ -2372,6 +2400,7 @@ getNamespaces(int *numNamespaces) + nsinfo[0].dobj.name = strdup("public"); + nsinfo[0].rolname = strdup(""); + nsinfo[0].nspacl = strdup(""); ++ nsinfo[0].seclabel = strdup(""); + + selectDumpableNamespace(&nsinfo[0]); + +@@ -2382,6 +2411,7 @@ getNamespaces(int *numNamespaces) + nsinfo[1].dobj.name = strdup("pg_catalog"); + nsinfo[1].rolname = strdup(""); + nsinfo[1].nspacl = strdup(""); ++ nsinfo[1].seclabel = strdup(""); + + selectDumpableNamespace(&nsinfo[1]); + +@@ -2402,8 +2432,8 @@ getNamespaces(int *numNamespaces) + */ + appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, " + "(%s nspowner) AS rolname, " +- "nspacl FROM pg_namespace", +- username_subquery); ++ "nspacl, %s AS seclabel FROM pg_namespace", ++ username_subquery, security_label ? "security_label" : "NULL"); + + res = PQexec(g_conn, query->data); + check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); +@@ -2417,6 +2447,7 @@ getNamespaces(int *numNamespaces) + i_nspname = PQfnumber(res, "nspname"); + i_rolname = PQfnumber(res, "rolname"); + i_nspacl = PQfnumber(res, "nspacl"); ++ i_seclabel = PQfnumber(res, "seclabel"); + + for (i = 0; i < ntups; i++) + { +@@ -2427,6 +2458,7 @@ getNamespaces(int *numNamespaces) + nsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_nspname)); + nsinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname)); + nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl)); ++ nsinfo[i].seclabel = strdup(PQgetvalue(res, i, i_seclabel)); + + /* Decide whether to dump this namespace */ + selectDumpableNamespace(&nsinfo[i]); +@@ -2515,6 +2547,7 @@ getTypes(int *numTypes) + int i_typtype; + int i_typisdefined; + int i_isarray; ++ int i_seclabel; + + /* + * we include even the built-in types because those may be used as array +@@ -2547,8 +2580,10 @@ getTypes(int *numTypes) + "typtype, typisdefined, " + "typname[0] = '_' AND typelem != 0 AND " + "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray " ++ ",%s AS seclabel " + "FROM pg_type", +- username_subquery); ++ username_subquery, ++ security_label ? "security_label" : "NULL"); + } + else if (g_fout->remoteVersion >= 70300) + { +@@ -2561,6 +2596,7 @@ getTypes(int *numTypes) + "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " + "typtype, typisdefined, " + "typname[0] = '_' AND typelem != 0 AS isarray " ++ ",NULL AS seclabel " + "FROM pg_type", + username_subquery); + } +@@ -2575,6 +2611,7 @@ getTypes(int *numTypes) + "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " + "typtype, typisdefined, " + "typname[0] = '_' AND typelem != 0 AS isarray " ++ ",NULL AS seclabel " + "FROM pg_type", + username_subquery); + } +@@ -2591,6 +2628,7 @@ getTypes(int *numTypes) + "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " + "typtype, typisdefined, " + "typname[0] = '_' AND typelem != 0 AS isarray " ++ ",NULL AS seclabel " + "FROM pg_type", + username_subquery); + } +@@ -2615,6 +2653,7 @@ getTypes(int *numTypes) + i_typtype = PQfnumber(res, "typtype"); + i_typisdefined = PQfnumber(res, "typisdefined"); + i_isarray = PQfnumber(res, "isarray"); ++ i_seclabel = PQfnumber(res, "seclabel"); + + for (i = 0; i < ntups; i++) + { +@@ -2642,6 +2681,8 @@ getTypes(int *numTypes) + else + tyinfo[i].isArray = false; + ++ tyinfo[i].seclabel = strdup(PQgetvalue(res, i, i_seclabel)); ++ + /* Decide whether we want to dump it */ + selectDumpableType(&tyinfo[i]); + +@@ -3407,6 +3448,7 @@ getTables(int *numTables) + int i_relhasindex; + int i_relhasrules; + int i_relhasoids; ++ int i_relhassecids; + int i_relfrozenxid; + int i_owning_tab; + int i_owning_col; +@@ -3414,6 +3456,7 @@ getTables(int *numTables) + int i_reloptions; + int i_toastreloptions; + int i_reloftype; ++ int i_seclabel; + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); +@@ -3450,6 +3493,7 @@ getTables(int *numTables) + "(%s c.relowner) AS rolname, " + "c.relchecks, c.relhastriggers, " + "c.relhasindex, c.relhasrules, c.relhasoids, " ++ "c.relhassecids, " + "c.relfrozenxid, " + "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " + "d.refobjid AS owning_tab, " +@@ -3457,6 +3501,7 @@ getTables(int *numTables) + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " + "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " ++ ",%s AS seclabel " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3467,6 +3512,7 @@ getTables(int *numTables) + "WHERE c.relkind in ('%c', '%c', '%c', '%c') " + "ORDER BY c.oid", + username_subquery, ++ security_label ? "c.security_label" : "NULL", + RELKIND_SEQUENCE, + RELKIND_RELATION, RELKIND_SEQUENCE, + RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); +@@ -3483,6 +3529,7 @@ getTables(int *numTables) + "(%s c.relowner) AS rolname, " + "c.relchecks, c.relhastriggers, " + "c.relhasindex, c.relhasrules, c.relhasoids, " ++ "false AS relhasecids, " + "c.relfrozenxid, " + "NULL AS reloftype, " + "d.refobjid AS owning_tab, " +@@ -3490,6 +3537,7 @@ getTables(int *numTables) + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " + "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " ++ ",%s AS seclabel " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3500,6 +3548,7 @@ getTables(int *numTables) + "WHERE c.relkind in ('%c', '%c', '%c', '%c') " + "ORDER BY c.oid", + username_subquery, ++ security_label ? "security_label" : "NULL", + RELKIND_SEQUENCE, + RELKIND_RELATION, RELKIND_SEQUENCE, + RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); +@@ -3516,6 +3565,7 @@ getTables(int *numTables) + "(%s relowner) AS rolname, " + "relchecks, (reltriggers <> 0) AS relhastriggers, " + "relhasindex, relhasrules, relhasoids, " ++ "false AS relhassecids, " + "relfrozenxid, " + "NULL AS reloftype, " + "d.refobjid AS owning_tab, " +@@ -3523,6 +3573,7 @@ getTables(int *numTables) + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " + "NULL AS toast_reloptions " ++ ",NULL AS seclabel " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3548,6 +3599,7 @@ getTables(int *numTables) + "(%s relowner) AS rolname, " + "relchecks, (reltriggers <> 0) AS relhastriggers, " + "relhasindex, relhasrules, relhasoids, " ++ "false AS relhassecids, " + "0 AS relfrozenxid, " + "NULL AS reloftype, " + "d.refobjid AS owning_tab, " +@@ -3555,6 +3607,7 @@ getTables(int *numTables) + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "NULL AS reloptions, " + "NULL AS toast_reloptions " ++ ",NULL AS seclabel " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3580,6 +3633,7 @@ getTables(int *numTables) + "(%s relowner) AS rolname, " + "relchecks, (reltriggers <> 0) AS relhastriggers, " + "relhasindex, relhasrules, relhasoids, " ++ "false AS relhassecids, " + "0 AS relfrozenxid, " + "NULL AS reloftype, " + "d.refobjid AS owning_tab, " +@@ -3587,6 +3641,7 @@ getTables(int *numTables) + "NULL AS reltablespace, " + "NULL AS reloptions, " + "NULL AS toast_reloptions " ++ ",NULL AS seclabel " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3608,6 +3663,7 @@ getTables(int *numTables) + "(%s relowner) AS rolname, " + "relchecks, (reltriggers <> 0) AS relhastriggers, " + "relhasindex, relhasrules, relhasoids, " ++ "false AS relhassecids, " + "0 AS relfrozenxid, " + "NULL AS reloftype, " + "NULL::oid AS owning_tab, " +@@ -3615,6 +3671,7 @@ getTables(int *numTables) + "NULL AS reltablespace, " + "NULL AS reloptions, " + "NULL AS toast_reloptions " ++ ",NULL AS seclabel " + "FROM pg_class " + "WHERE relkind IN ('%c', '%c', '%c') " + "ORDER BY oid", +@@ -3631,6 +3688,7 @@ getTables(int *numTables) + "relchecks, (reltriggers <> 0) AS relhastriggers, " + "relhasindex, relhasrules, " + "'t'::bool AS relhasoids, " ++ "'f'::bool AS relhassecids, " + "0 AS relfrozenxid, " + "NULL AS reloftype, " + "NULL::oid AS owning_tab, " +@@ -3638,6 +3696,7 @@ getTables(int *numTables) + "NULL AS reltablespace, " + "NULL AS reloptions, " + "NULL AS toast_reloptions " ++ ",NULL AS seclabel " + "FROM pg_class " + "WHERE relkind IN ('%c', '%c', '%c') " + "ORDER BY oid", +@@ -3671,6 +3730,7 @@ getTables(int *numTables) + "NULL AS reltablespace, " + "NULL AS reloptions, " + "NULL AS toast_reloptions " ++ ",NULL AS seclabel " + "FROM pg_class c " + "WHERE relkind IN ('%c', '%c') " + "ORDER BY oid", +@@ -3709,6 +3769,7 @@ getTables(int *numTables) + i_relhasindex = PQfnumber(res, "relhasindex"); + i_relhasrules = PQfnumber(res, "relhasrules"); + i_relhasoids = PQfnumber(res, "relhasoids"); ++ i_relhassecids = PQfnumber(res, "relhassecids"); + i_relfrozenxid = PQfnumber(res, "relfrozenxid"); + i_owning_tab = PQfnumber(res, "owning_tab"); + i_owning_col = PQfnumber(res, "owning_col"); +@@ -3716,6 +3777,7 @@ getTables(int *numTables) + i_reloptions = PQfnumber(res, "reloptions"); + i_toastreloptions = PQfnumber(res, "toast_reloptions"); + i_reloftype = PQfnumber(res, "reloftype"); ++ i_seclabel = PQfnumber(res, "seclabel"); + + if (lockWaitTimeout && g_fout->remoteVersion >= 70300) + { +@@ -3748,6 +3810,7 @@ getTables(int *numTables) + tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0); + tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0); + tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0); ++ tblinfo[i].hassecids = (strcmp(PQgetvalue(res, i, i_relhassecids), "t")==0); + tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid)); + if (PQgetisnull(res, i, i_reloftype)) + tblinfo[i].reloftype = NULL; +@@ -3767,6 +3830,7 @@ getTables(int *numTables) + tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace)); + tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions)); + tblinfo[i].toast_reloptions = strdup(PQgetvalue(res, i, i_toastreloptions)); ++ tblinfo[i].rellabel = strdup(PQgetvalue(res, i, i_seclabel)); + + /* other fields were zeroed above */ + +@@ -5082,6 +5146,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + int i_attalign; + int i_attislocal; + int i_attoptions; ++ int i_seclabel; + PGresult *res; + int ntups; + bool hasdefaults; +@@ -5128,11 +5193,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + "a.attlen, a.attalign, a.attislocal, " + "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " + "array_to_string(attoptions, ', ') AS attoptions " ++ ",%s AS seclabel " + "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::pg_catalog.oid " + "AND a.attnum > 0::pg_catalog.int2 " + "ORDER BY a.attrelid, a.attnum", ++ security_label ? "a.security_label" : "NULL", + tbinfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 70300) +@@ -5144,6 +5211,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + "a.attlen, a.attalign, a.attislocal, " + "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " + "'' AS attoptions " ++ ",NULL AS seclabel " + "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::pg_catalog.oid " +@@ -5165,6 +5233,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + "a.attalign, false AS attislocal, " + "format_type(t.oid,a.atttypmod) AS atttypname, " + "'' AS attoptions " ++ ",NULL AS seclabel " + "FROM pg_attribute a LEFT JOIN pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::oid " +@@ -5183,6 +5252,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + "false AS attislocal, " + "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, " + "'' AS attoptions " ++ ",NULL AS seclabel " + "FROM pg_attribute a " + "WHERE attrelid = '%u'::oid " + "AND attnum > 0::int2 " +@@ -5209,6 +5279,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + i_attalign = PQfnumber(res, "attalign"); + i_attislocal = PQfnumber(res, "attislocal"); + i_attoptions = PQfnumber(res, "attoptions"); ++ i_seclabel = PQfnumber(res, "seclabel"); + + tbinfo->numatts = ntups; + tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); +@@ -5227,6 +5298,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); + tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool)); + tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool)); ++ tbinfo->attlabels = (char **) malloc(ntups * sizeof(char *)); + hasdefaults = false; + + for (j = 0; j < ntups; j++) +@@ -5256,6 +5328,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + tbinfo->inhAttrs[j] = false; + tbinfo->inhAttrDef[j] = false; + tbinfo->inhNotNull[j] = false; ++ tbinfo->attlabels[j] = strdup(PQgetvalue(res, j, i_seclabel)); + } + + PQclear(res); +@@ -6146,7 +6219,7 @@ dumpComment(Archive *fout, const char *target, + */ + ArchiveEntry(fout, nilCatalogId, createDumpId(), + target, namespace, NULL, owner, +- false, "COMMENT", SECTION_NONE, ++ false, false, "COMMENT", SECTION_NONE, + query->data, "", NULL, + &(dumpId), 1, + NULL, NULL); +@@ -6207,7 +6280,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, + target->data, + tbinfo->dobj.namespace->dobj.name, + NULL, tbinfo->rolname, +- false, "COMMENT", SECTION_NONE, ++ false, false, "COMMENT", SECTION_NONE, + query->data, "", NULL, + &(tbinfo->dobj.dumpId), 1, + NULL, NULL); +@@ -6229,7 +6302,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, + target->data, + tbinfo->dobj.namespace->dobj.name, + NULL, tbinfo->rolname, +- false, "COMMENT", SECTION_NONE, ++ false, false, "COMMENT", SECTION_NONE, + query->data, "", NULL, + &(tbinfo->dobj.dumpId), 1, + NULL, NULL); +@@ -6509,7 +6582,7 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) + case DO_BLOB_DATA: + ArchiveEntry(fout, dobj->catId, dobj->dumpId, + dobj->name, NULL, NULL, "", +- false, "BLOBS", SECTION_DATA, ++ false, false, "BLOBS", SECTION_DATA, + "", "", NULL, + dobj->dependencies, dobj->nDeps, + dumpBlobs, NULL); +@@ -6549,7 +6622,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) + nspinfo->dobj.name, + NULL, NULL, + nspinfo->rolname, +- false, "SCHEMA", SECTION_PRE_DATA, ++ false, false, "SCHEMA", SECTION_PRE_DATA, + q->data, delq->data, NULL, + nspinfo->dobj.dependencies, nspinfo->dobj.nDeps, + NULL, NULL); +@@ -6561,6 +6634,9 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) + NULL, nspinfo->rolname, + nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); + ++ dumpSecLabel(fout, nspinfo->dobj.dumpId, ++ q->data, NULL, nspinfo->seclabel); ++ + dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA", + qnspname, NULL, nspinfo->dobj.name, NULL, + nspinfo->rolname, nspinfo->nspacl); +@@ -6678,7 +6754,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + NULL, +- tyinfo->rolname, false, ++ tyinfo->rolname, false, false, + "TYPE", SECTION_PRE_DATA, + q->data, delq->data, NULL, + tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, +@@ -6692,6 +6768,12 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + ++ /* Dump type security label */ ++ dumpSecLabel(fout, tyinfo->dobj.dumpId, ++ q->data, ++ tyinfo->dobj.namespace->dobj.name, ++ tyinfo->seclabel); ++ + PQclear(res); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); +@@ -7054,7 +7136,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + NULL, +- tyinfo->rolname, false, ++ tyinfo->rolname, false, false, + "TYPE", SECTION_PRE_DATA, + q->data, delq->data, NULL, + tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, +@@ -7068,6 +7150,12 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + ++ /* Dump type security label */ ++ dumpSecLabel(fout, tyinfo->dobj.dumpId, ++ q->data, ++ tyinfo->dobj.namespace->dobj.name, ++ tyinfo->seclabel); ++ + PQclear(res); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); +@@ -7178,7 +7266,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + NULL, +- tyinfo->rolname, false, ++ tyinfo->rolname, false, false, + "DOMAIN", SECTION_PRE_DATA, + q->data, delq->data, NULL, + tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, +@@ -7192,6 +7280,12 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + ++ /* Dump domain security label */ ++ dumpSecLabel(fout, tyinfo->dobj.dumpId, ++ q->data, ++ tyinfo->dobj.namespace->dobj.name, ++ tyinfo->seclabel); ++ + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); + destroyPQExpBuffer(query); +@@ -7283,7 +7377,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + NULL, +- tyinfo->rolname, false, ++ tyinfo->rolname, false, false, + "TYPE", SECTION_PRE_DATA, + q->data, delq->data, NULL, + tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, +@@ -7298,6 +7392,12 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + ++ /* Dump type security label */ ++ dumpSecLabel(fout, tyinfo->dobj.dumpId, ++ q->data, ++ tyinfo->dobj.namespace->dobj.name, ++ tyinfo->seclabel); ++ + PQclear(res); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); +@@ -7402,7 +7502,7 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo) + target->data, + tyinfo->dobj.namespace->dobj.name, + NULL, tyinfo->rolname, +- false, "COMMENT", SECTION_NONE, ++ false, false, "COMMENT", SECTION_NONE, + query->data, "", NULL, + &(tyinfo->dobj.dumpId), 1, + NULL, NULL); +@@ -7454,7 +7554,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo) + stinfo->dobj.name, + stinfo->dobj.namespace->dobj.name, + NULL, +- stinfo->baseType->rolname, false, ++ stinfo->baseType->rolname, false, false, + "SHELL TYPE", SECTION_PRE_DATA, + q->data, "", NULL, + stinfo->dobj.dependencies, stinfo->dobj.nDeps, +@@ -7609,7 +7709,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) + ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId, + plang->dobj.name, + lanschema, NULL, plang->lanowner, +- false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA, ++ false, false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA, + defqry->data, delqry->data, NULL, + plang->dobj.dependencies, plang->dobj.nDeps, + NULL, NULL); +@@ -7795,6 +7895,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + char **argmodes = NULL; + char **argnames = NULL; + char **configitems = NULL; ++ char *proseclabel; + int nconfigitems = 0; + int i; + +@@ -7825,8 +7926,10 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "proiswindow, provolatile, proisstrict, prosecdef, " + "proconfig, procost, prorows, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ ",%s AS seclabel " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", ++ security_label ? "security_label" : "NULL", + finfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 80300) +@@ -7838,6 +7941,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "provolatile, proisstrict, prosecdef, " + "proconfig, procost, prorows, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ ",NULL AS seclabel " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +@@ -7851,6 +7955,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ ",NULL AS seclabel " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +@@ -7866,6 +7971,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ ",NULL AS seclabel " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +@@ -7881,6 +7987,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ ",NULL AS seclabel " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +@@ -7898,6 +8005,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "false AS prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " + "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " ++ ",NULL AS seclabel " + "FROM pg_proc " + "WHERE oid = '%u'::oid", + finfo->dobj.catId.oid); +@@ -7915,6 +8023,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "false AS prosecdef, " + "NULL AS proconfig, 0 AS procost, 0 AS prorows, " + "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " ++ ",NULL AS seclabel " + "FROM pg_proc " + "WHERE oid = '%u'::oid", + finfo->dobj.catId.oid); +@@ -7959,6 +8068,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + procost = PQgetvalue(res, 0, PQfnumber(res, "procost")); + prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); + lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); ++ proseclabel = PQgetvalue(res, 0, PQfnumber(res, "seclabel")); + + /* + * See backend/commands/functioncmds.c for details of how the 'AS' clause +@@ -8169,7 +8279,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + funcsig_tag, + finfo->dobj.namespace->dobj.name, + NULL, +- finfo->rolname, false, ++ finfo->rolname, false, false, + "FUNCTION", SECTION_PRE_DATA, + q->data, delqry->data, NULL, + finfo->dobj.dependencies, finfo->dobj.nDeps, +@@ -8182,6 +8292,12 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + finfo->dobj.namespace->dobj.name, finfo->rolname, + finfo->dobj.catId, 0, finfo->dobj.dumpId); + ++ /* Dump Function security label */ ++ dumpSecLabel(fout, finfo->dobj.dumpId, ++ q->data, ++ finfo->dobj.namespace->dobj.name, ++ proseclabel); ++ + dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION", + funcsig, NULL, funcsig_tag, + finfo->dobj.namespace->dobj.name, +@@ -8323,7 +8439,7 @@ dumpCast(Archive *fout, CastInfo *cast) + ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId, + castsig->data, + "pg_catalog", NULL, "", +- false, "CAST", SECTION_PRE_DATA, ++ false, false, "CAST", SECTION_PRE_DATA, + defqry->data, delqry->data, NULL, + cast->dobj.dependencies, cast->dobj.nDeps, + NULL, NULL); +@@ -8567,7 +8683,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) + oprinfo->dobj.namespace->dobj.name, + NULL, + oprinfo->rolname, +- false, "OPERATOR", SECTION_PRE_DATA, ++ false, false, "OPERATOR", SECTION_PRE_DATA, + q->data, delq->data, NULL, + oprinfo->dobj.dependencies, oprinfo->dobj.nDeps, + NULL, NULL); +@@ -9026,7 +9142,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) + opcinfo->dobj.namespace->dobj.name, + NULL, + opcinfo->rolname, +- false, "OPERATOR CLASS", SECTION_PRE_DATA, ++ false, false, "OPERATOR CLASS", SECTION_PRE_DATA, + q->data, delq->data, NULL, + opcinfo->dobj.dependencies, opcinfo->dobj.nDeps, + NULL, NULL); +@@ -9307,7 +9423,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) + opfinfo->dobj.namespace->dobj.name, + NULL, + opfinfo->rolname, +- false, "OPERATOR FAMILY", SECTION_PRE_DATA, ++ false, false, "OPERATOR FAMILY", SECTION_PRE_DATA, + q->data, delq->data, NULL, + opfinfo->dobj.dependencies, opfinfo->dobj.nDeps, + NULL, NULL); +@@ -9423,7 +9539,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) + convinfo->dobj.namespace->dobj.name, + NULL, + convinfo->rolname, +- false, "CONVERSION", SECTION_PRE_DATA, ++ false, false, "CONVERSION", SECTION_PRE_DATA, + q->data, delq->data, NULL, + convinfo->dobj.dependencies, convinfo->dobj.nDeps, + NULL, NULL); +@@ -9504,11 +9620,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + int i_aggtranstype; + int i_agginitval; + int i_convertok; ++ int i_seclabel; + const char *aggtransfn; + const char *aggfinalfn; + const char *aggsortop; + const char *aggtranstype; + const char *agginitval; ++ const char *seclabel; + bool convertok; + + /* Skip if not to be dumped */ +@@ -9531,9 +9649,11 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + "aggsortop::pg_catalog.regoperator, " + "agginitval, " + "'t'::boolean AS convertok " ++ ",%s AS seclabel " + "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " + "WHERE a.aggfnoid = p.oid " + "AND p.oid = '%u'::pg_catalog.oid", ++ security_label ? "p.security_label" : "NULL", + agginfo->aggfn.dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 70300) +@@ -9543,6 +9663,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + "0 AS aggsortop, " + "agginitval, " + "'t'::boolean AS convertok " ++ ",NULL AS seclabel " + "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " + "WHERE a.aggfnoid = p.oid " + "AND p.oid = '%u'::pg_catalog.oid", +@@ -9555,6 +9676,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + "0 AS aggsortop, " + "agginitval, " + "'t'::boolean AS convertok " ++ ",NULL AS seclabel " + "FROM pg_aggregate " + "WHERE oid = '%u'::oid", + agginfo->aggfn.dobj.catId.oid); +@@ -9567,6 +9689,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + "0 AS aggsortop, " + "agginitval1 AS agginitval, " + "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok " ++ ",NULL AS seclabel " + "FROM pg_aggregate " + "WHERE oid = '%u'::oid", + agginfo->aggfn.dobj.catId.oid); +@@ -9592,6 +9715,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + i_aggtranstype = PQfnumber(res, "aggtranstype"); + i_agginitval = PQfnumber(res, "agginitval"); + i_convertok = PQfnumber(res, "convertok"); ++ i_seclabel = PQfnumber(res, "seclabel"); + + aggtransfn = PQgetvalue(res, 0, i_aggtransfn); + aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn); +@@ -9599,6 +9723,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + aggtranstype = PQgetvalue(res, 0, i_aggtranstype); + agginitval = PQgetvalue(res, 0, i_agginitval); + convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't'); ++ seclabel = PQgetvalue(res, 0, i_seclabel); + + aggsig = format_aggregate_signature(agginfo, fout, true); + aggsig_tag = format_aggregate_signature(agginfo, fout, false); +@@ -9667,7 +9792,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + agginfo->aggfn.dobj.namespace->dobj.name, + NULL, + agginfo->aggfn.rolname, +- false, "AGGREGATE", SECTION_PRE_DATA, ++ false, false, "AGGREGATE", SECTION_PRE_DATA, + q->data, delq->data, NULL, + agginfo->aggfn.dobj.dependencies, agginfo->aggfn.dobj.nDeps, + NULL, NULL); +@@ -9679,6 +9804,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname, + agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId); + ++ /* Dump Aggregate security label */ ++ dumpSecLabel(fout, agginfo->aggfn.dobj.dumpId, ++ q->data, ++ agginfo->aggfn.dobj.namespace->dobj.name, ++ seclabel); ++ + /* + * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL + * command look like a function's GRANT; in particular this affects the +@@ -9755,7 +9886,7 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) + prsinfo->dobj.namespace->dobj.name, + NULL, + "", +- false, "TEXT SEARCH PARSER", SECTION_PRE_DATA, ++ false, false, "TEXT SEARCH PARSER", SECTION_PRE_DATA, + q->data, delq->data, NULL, + prsinfo->dobj.dependencies, prsinfo->dobj.nDeps, + NULL, NULL); +@@ -9847,7 +9978,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) + dictinfo->dobj.namespace->dobj.name, + NULL, + dictinfo->rolname, +- false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA, ++ false, false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA, + q->data, delq->data, NULL, + dictinfo->dobj.dependencies, dictinfo->dobj.nDeps, + NULL, NULL); +@@ -9907,7 +10038,7 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) + tmplinfo->dobj.namespace->dobj.name, + NULL, + "", +- false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA, ++ false, false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA, + q->data, delq->data, NULL, + tmplinfo->dobj.dependencies, tmplinfo->dobj.nDeps, + NULL, NULL); +@@ -10040,7 +10171,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) + cfginfo->dobj.namespace->dobj.name, + NULL, + cfginfo->rolname, +- false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA, ++ false, false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA, + q->data, delq->data, NULL, + cfginfo->dobj.dependencies, cfginfo->dobj.nDeps, + NULL, NULL); +@@ -10096,7 +10227,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) + NULL, + NULL, + fdwinfo->rolname, +- false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA, ++ false, false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA, + q->data, delq->data, NULL, + fdwinfo->dobj.dependencies, fdwinfo->dobj.nDeps, + NULL, NULL); +@@ -10183,7 +10314,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) + NULL, + NULL, + srvinfo->rolname, +- false, "SERVER", SECTION_PRE_DATA, ++ false, false, "SERVER", SECTION_PRE_DATA, + q->data, delq->data, NULL, + srvinfo->dobj.dependencies, srvinfo->dobj.nDeps, + NULL, NULL); +@@ -10279,7 +10410,7 @@ dumpUserMappings(Archive *fout, const char *target, + tag->data, + namespace, + NULL, +- owner, false, ++ owner, false, false, + "USER MAPPING", SECTION_PRE_DATA, + q->data, delq->data, NULL, + &dumpId, 1, +@@ -10350,7 +10481,7 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) + daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL, + NULL, + daclinfo->defaclrole, +- false, "DEFAULT ACL", SECTION_NONE, ++ false, false, "DEFAULT ACL", SECTION_NONE, + q->data, "", NULL, + daclinfo->dobj.dependencies, daclinfo->dobj.nDeps, + NULL, NULL); +@@ -10407,7 +10538,7 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, + tag, nspname, + NULL, + owner ? owner : "", +- false, "ACL", SECTION_NONE, ++ false, false, "ACL", SECTION_NONE, + sql->data, "", NULL, + &(objDumpId), 1, + NULL, NULL); +@@ -10416,6 +10547,123 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, + } + + /* ++ * dumpSecLabel ++ * ++ * write out security label of the objects ++ */ ++static void ++dumpSecLabel(Archive *fout, DumpId objDumpId, ++ const char *target, const char *namespace, ++ const char *seclabel) ++{ ++ PQExpBuffer qry; ++ ++ /* Do nothing, if security label dump is not given */ ++ if (!security_label || strlen(seclabel) == 0) ++ return; ++ ++ /* Do nothing, if --data-only for schemas, or --schema-only for blobs */ ++ if (strncmp(target, "LARGE OBJECT", 13) == 0) ++ { ++ if (schemaOnly) ++ return; ++ } ++ else ++ { ++ if (dataOnly) ++ return; ++ } ++ ++ /* Make ALTER xxx SECURITY LABEL TO command */ ++ qry = createPQExpBuffer(); ++ ++ appendPQExpBuffer(qry, "ALTER %s SECURITY LABEL TO '%s';", ++ target, seclabel); ++ ++ ArchiveEntry(fout, ++ nilCatalogId, /* catalog ID */ ++ createDumpId(), /* dump ID */ ++ target, /* name */ ++ namespace, /* namespace */ ++ NULL, /* tablespace */ ++ "", /* owner */ ++ false, /* with oids */ ++ false, /* with secids */ ++ "LABEL", /* desc */ ++ SECTION_NONE, /* section */ ++ qry->data, /* create */ ++ "", /* delete*/ ++ NULL, /* copy */ ++ &(objDumpId), /* dependency */ ++ 1, /* # deps */ ++ NULL, /* dumper Func */ ++ NULL); /* dumper Arg */ ++ ++ destroyPQExpBuffer(qry); ++} ++ ++/* ++ * dumpTableSecLabel ++ * ++ * write out security label of the table ++ */ ++static void ++dumpTableSecLabel(Archive *fout, DumpId objDumpId, ++ const char *namespace, ++ const char *table_name, ++ const char **column_names, int ncolumns, ++ const char *table_label, const char **column_labels) ++{ ++ PQExpBuffer qry; ++ PQExpBuffer tag; ++ int i; ++ ++ if (!security_label || dataOnly) ++ return; ++ ++ /* Make ALTER xxx SECURITY LABEL TO command */ ++ qry = createPQExpBuffer(); ++ tag = createPQExpBuffer(); ++ ++ appendPQExpBuffer(tag, "TABLE %s", table_name); ++ ++ if (strlen(table_label) > 0) ++ appendPQExpBuffer(qry, "ALTER TABLE %s SECURITY LABEL TO '%s';\n", ++ table_name, table_label); ++ for (i = 0; i < ncolumns; i++) ++ { ++ if (strlen(column_labels[i]) > 0) ++ appendPQExpBuffer(qry, "ALTER TABLE %s ALTER %s SECURITY LABEL TO '%s';\n", ++ table_name, ++ column_names[i], ++ column_labels[i]); ++ } ++ ++ if (qry->len > 0) ++ ArchiveEntry(fout, ++ nilCatalogId, /* catalog ID */ ++ createDumpId(), /* dump ID */ ++ tag->data, /* name */ ++ namespace, /* namespace */ ++ NULL, /* tablespace */ ++ "", /* owner */ ++ false, /* with oids */ ++ false, /* with secids */ ++ "LABEL", /* desc */ ++ SECTION_NONE, /* section */ ++ qry->data, /* create */ ++ "", /* delete*/ ++ NULL, /* copy */ ++ &(objDumpId), /* dependency */ ++ 1, /* # deps */ ++ NULL, /* dumper Func */ ++ NULL); /* dumper Arg */ ++ ++ destroyPQExpBuffer(qry); ++ destroyPQExpBuffer(tag); ++} ++ ++/* + * dumpTable + * write out to fout the declarations (not data) of a user-defined table + */ +@@ -10571,7 +10819,6 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) + + appendPQExpBuffer(q, "CREATE VIEW %s AS\n %s\n", + fmtId(tbinfo->dobj.name), viewdef); +- + PQclear(res); + } + else +@@ -10922,6 +11169,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) + (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace, + tbinfo->rolname, + (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false, ++ (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hassecids : false, + reltypename, SECTION_PRE_DATA, + q->data, delq->data, NULL, + tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, +@@ -10942,6 +11190,27 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) + dumpTableConstraintComment(fout, constr); + } + ++ /* Dump relation/attribute's security labels */ ++ if (tbinfo->relkind == RELKIND_RELATION) ++ { ++ dumpTableSecLabel(fout, tbinfo->dobj.dumpId, ++ tbinfo->dobj.namespace->dobj.name, ++ tbinfo->dobj.name, ++ tbinfo->attnames, tbinfo->numatts, ++ tbinfo->rellabel, tbinfo->attlabels); ++ } ++ else ++ { ++ PQExpBuffer target = createPQExpBuffer(); ++ ++ appendPQExpBuffer(target, "%s %s", reltypename, fmtId(tbinfo->dobj.name)); ++ dumpSecLabel(fout, tbinfo->dobj.dumpId, ++ target->data, ++ tbinfo->dobj.namespace->dobj.name, ++ tbinfo->rellabel); ++ destroyPQExpBuffer(target); ++ } ++ + destroyPQExpBuffer(query); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); +@@ -10990,7 +11259,7 @@ dumpAttrDef(Archive *fout, AttrDefInfo *adinfo) + tbinfo->dobj.namespace->dobj.name, + NULL, + tbinfo->rolname, +- false, "DEFAULT", SECTION_PRE_DATA, ++ false, false, "DEFAULT", SECTION_PRE_DATA, + q->data, delq->data, NULL, + adinfo->dobj.dependencies, adinfo->dobj.nDeps, + NULL, NULL); +@@ -11086,7 +11355,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) + indxinfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + indxinfo->tablespace, +- tbinfo->rolname, false, ++ tbinfo->rolname, false, false, + "INDEX", SECTION_POST_DATA, + q->data, delq->data, NULL, + indxinfo->dobj.dependencies, indxinfo->dobj.nDeps, +@@ -11211,7 +11480,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) + coninfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + indxinfo->tablespace, +- tbinfo->rolname, false, ++ tbinfo->rolname, false, false, + "CONSTRAINT", SECTION_POST_DATA, + q->data, delq->data, NULL, + coninfo->dobj.dependencies, coninfo->dobj.nDeps, +@@ -11244,7 +11513,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) + coninfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + NULL, +- tbinfo->rolname, false, ++ tbinfo->rolname, false, false, + "FK CONSTRAINT", SECTION_POST_DATA, + q->data, delq->data, NULL, + coninfo->dobj.dependencies, coninfo->dobj.nDeps, +@@ -11279,7 +11548,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) + coninfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + NULL, +- tbinfo->rolname, false, ++ tbinfo->rolname, false, false, + "CHECK CONSTRAINT", SECTION_POST_DATA, + q->data, delq->data, NULL, + coninfo->dobj.dependencies, coninfo->dobj.nDeps, +@@ -11315,7 +11584,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) + coninfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + NULL, +- tyinfo->rolname, false, ++ tyinfo->rolname, false, false, + "CHECK CONSTRAINT", SECTION_POST_DATA, + q->data, delq->data, NULL, + coninfo->dobj.dependencies, coninfo->dobj.nDeps, +@@ -11601,7 +11870,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) + tbinfo->dobj.namespace->dobj.name, + NULL, + tbinfo->rolname, +- false, "SEQUENCE", SECTION_PRE_DATA, ++ false, false, "SEQUENCE", SECTION_PRE_DATA, + query->data, delqry->data, NULL, + tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, + NULL, NULL); +@@ -11637,7 +11906,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) + tbinfo->dobj.namespace->dobj.name, + NULL, + tbinfo->rolname, +- false, "SEQUENCE OWNED BY", SECTION_PRE_DATA, ++ false, false, "SEQUENCE OWNED BY", SECTION_PRE_DATA, + query->data, "", NULL, + &(tbinfo->dobj.dumpId), 1, + NULL, NULL); +@@ -11650,6 +11919,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) + dumpComment(fout, query->data, + tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, + tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); ++ ++ /* Dump sequence security label */ ++ dumpSecLabel(fout, tbinfo->dobj.dumpId, ++ query->data, ++ tbinfo->dobj.namespace->dobj.name, ++ tbinfo->rellabel); + } + + if (!schemaOnly) +@@ -11665,7 +11940,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) + tbinfo->dobj.namespace->dobj.name, + NULL, + tbinfo->rolname, +- false, "SEQUENCE SET", SECTION_PRE_DATA, ++ false, false, "SEQUENCE SET", SECTION_PRE_DATA, + query->data, "", NULL, + &(tbinfo->dobj.dumpId), 1, + NULL, NULL); +@@ -11849,7 +12124,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) + tginfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + NULL, +- tbinfo->rolname, false, ++ tbinfo->rolname, false, false, + "TRIGGER", SECTION_POST_DATA, + query->data, delqry->data, NULL, + tginfo->dobj.dependencies, tginfo->dobj.nDeps, +@@ -11969,7 +12244,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) + rinfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + NULL, +- tbinfo->rolname, false, ++ tbinfo->rolname, false, false, + "RULE", SECTION_POST_DATA, + cmd->data, delcmd->data, NULL, + rinfo->dobj.dependencies, rinfo->dobj.nDeps, +@@ -12334,7 +12609,7 @@ fmtQualifiedId(const char *schema, const char *id) + * "", not an invalid "()" column list. + */ + static const char * +-fmtCopyColumnList(const TableInfo *ti) ++fmtCopyColumnList(const TableInfo *ti, bool secids) + { + static PQExpBuffer q = NULL; + int numatts = ti->numatts; +@@ -12350,6 +12625,11 @@ fmtCopyColumnList(const TableInfo *ti) + + appendPQExpBuffer(q, "("); + needComma = false; ++ if (secids) ++ { ++ appendPQExpBuffer(q, "security_label"); ++ needComma = true; ++ } + for (i = 0; i < numatts; i++) + { + if (attisdropped[i]) +diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h +index c93bada..78a50bd 100644 +--- a/src/bin/pg_dump/pg_dump.h ++++ b/src/bin/pg_dump/pg_dump.h +@@ -137,6 +137,7 @@ typedef struct _namespaceInfo + DumpableObject dobj; + char *rolname; /* name of owner, or empty string */ + char *nspacl; ++ char *seclabel; + } NamespaceInfo; + + typedef struct _typeInfo +@@ -153,6 +154,7 @@ typedef struct _typeInfo + char typrelkind; /* 'r', 'v', 'c', etc */ + char typtype; /* 'b', 'c', etc */ + bool isArray; /* true if auto-generated array type */ ++ char *seclabel; + bool isDefined; /* true if typisdefined */ + /* If it's a dumpable base type, we create a "shell type" entry for it */ + struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */ +@@ -227,9 +229,11 @@ typedef struct _tableInfo + bool hasrules; /* does it have any rules? */ + bool hastriggers; /* does it have any triggers? */ + bool hasoids; /* does it have OIDs? */ ++ bool hassecids; /* does it have security-Id? */ + uint32 frozenxid; /* for restore frozen xid */ + int ncheck; /* # of CHECK expressions */ + char *reloftype; /* underlying type for typed table */ ++ char *rellabel; /* relation's security label */ + /* these two are set only if table is a sequence owned by a column: */ + Oid owning_tab; /* OID of table owning sequence */ + int owning_col; /* attr # of column owning sequence */ +@@ -252,6 +256,7 @@ typedef struct _tableInfo + char *attalign; /* attribute align, used by binary_upgrade */ + bool *attislocal; /* true if attr has local definition */ + char **attoptions; /* per-attribute options */ ++ char **attlabels; /* attribute's security label */ + + /* + * Note: we need to store per-attribute notnull, default, and constraint +@@ -287,6 +292,7 @@ typedef struct _tableDataInfo + DumpableObject dobj; + TableInfo *tdtable; /* link to table to dump */ + bool oids; /* include OIDs in data? */ ++ bool secids; /* include SecIDs in data? */ + } TableDataInfo; + + typedef struct _indxInfo +@@ -448,6 +454,7 @@ typedef struct _blobInfo + DumpableObject dobj; + char *rolname; + char *blobacl; ++ char *seclabel; + } BlobInfo; + + /* global decls */ +diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c +index 0c3f63f..83f1678 100644 +--- a/src/bin/pg_dump/pg_dumpall.c ++++ b/src/bin/pg_dump/pg_dumpall.c +@@ -69,6 +69,7 @@ static int disable_triggers = 0; + static int inserts = 0; + static int no_tablespaces = 0; + static int use_setsessauth = 0; ++static int security_label = 0; + static int server_version; + + static FILE *OPF; +@@ -132,6 +133,7 @@ main(int argc, char *argv[]) + {"no-tablespaces", no_argument, &no_tablespaces, 1}, + {"role", required_argument, NULL, 3}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"security-label", no_argument, &security_label, 1}, + + {NULL, 0, NULL, 0} + }; +@@ -285,6 +287,8 @@ main(int argc, char *argv[]) + no_tablespaces = 1; + else if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; ++ else if (strcmp(optarg, "security-label") == 0) ++ security_label = 1; + else + { + fprintf(stderr, +@@ -330,6 +334,8 @@ main(int argc, char *argv[]) + appendPQExpBuffer(pgdumpopts, " --no-tablespaces"); + if (use_setsessauth) + appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); ++ if (security_label) ++ appendPQExpBuffer(pgdumpopts, " --security-label"); + + if (optind < argc) + { +@@ -561,6 +567,7 @@ help(void) + printf(_(" --use-set-session-authorization\n" + " use SET SESSION AUTHORIZATION commands instead of\n" + " ALTER OWNER commands to set ownership\n")); ++ printf(_(" --security-label dump schema/data with security label\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); +@@ -949,6 +956,7 @@ dropTablespaces(PGconn *conn) + static void + dumpTablespaces(PGconn *conn) + { ++ PQExpBuffer qry = createPQExpBuffer(); + PGresult *res; + int i; + +@@ -957,31 +965,37 @@ dumpTablespaces(PGconn *conn) + * pg_xxx) + */ + if (server_version >= 90000) +- res = executeQuery(conn, "SELECT spcname, " ++ appendPQExpBuffer(qry, "SELECT spcname, " + "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " + "spclocation, spcacl, " + "array_to_string(spcoptions, ', ')," + "pg_catalog.shobj_description(oid, 'pg_tablespace') " ++ ",%s AS seclabel " + "FROM pg_catalog.pg_tablespace " + "WHERE spcname !~ '^pg_' " +- "ORDER BY 1"); ++ "ORDER BY 1", ++ security_label ? "security_label" : "NULL"); + else if (server_version >= 80200) +- res = executeQuery(conn, "SELECT spcname, " ++ appendPQExpBuffer(qry, "SELECT spcname, " + "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " + "spclocation, spcacl, null, " + "pg_catalog.shobj_description(oid, 'pg_tablespace') " ++ ",NULL AS seclabel" + "FROM pg_catalog.pg_tablespace " + "WHERE spcname !~ '^pg_' " + "ORDER BY 1"); + else +- res = executeQuery(conn, "SELECT spcname, " ++ appendPQExpBuffer(qry, "SELECT spcname, " + "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " + "spclocation, spcacl, " + "null, null " ++ ",NULL AS seclabel" + "FROM pg_catalog.pg_tablespace " + "WHERE spcname !~ '^pg_' " + "ORDER BY 1"); + ++ res = PQexec(conn, qry->data); ++ + if (PQntuples(res) > 0) + fprintf(OPF, "--\n-- Tablespaces\n--\n\n"); + +@@ -994,6 +1008,7 @@ dumpTablespaces(PGconn *conn) + char *spcacl = PQgetvalue(res, i, 3); + char *spcoptions = PQgetvalue(res, i, 4); + char *spccomment = PQgetvalue(res, i, 5); ++ char *spcseclabel = PQgetvalue(res, i, 6); + char *fspcname; + + /* needed for buildACLCommands() */ +@@ -1010,6 +1025,10 @@ dumpTablespaces(PGconn *conn) + appendPQExpBuffer(buf, "ALTER TABLESPACE %s SET (%s);\n", + fspcname, spcoptions); + ++ if (security_label && strlen(spcseclabel) > 0) ++ appendPQExpBuffer(buf, "ALTER TABLESPACE %s SECURITY LABEL TO '%s';\n", ++ fspcname, spcseclabel); ++ + if (!skip_acls && + !buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner, + "", server_version, buf)) +@@ -1153,48 +1172,48 @@ dumpCreateDB(PGconn *conn) + + /* Now collect all the information about databases to dump */ + if (server_version >= 80400) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "datcollate, datctype, datfrozenxid, " + "datistemplate, datacl, datconnlimit, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " ++ ",%s AS seclabel " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " +- "WHERE datallowconn ORDER BY 1"); ++ "WHERE datallowconn ORDER BY 1", ++ security_label ? "d.security_label" : "NULL"); + else if (server_version >= 80100) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, datconnlimit, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " ++ ",NULL AS seclabel " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 80000) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, -1 as datconnlimit, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " ++ ",NULL AS seclabel " + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70300) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, -1 as datconnlimit, " + "'pg_default' AS dattablespace " ++ ",NULL AS seclabel " + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70100) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(" + "(select usename from pg_shadow where usesysid=datdba), " + "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " +@@ -1202,6 +1221,7 @@ dumpCreateDB(PGconn *conn) + "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " + "datistemplate, '' as datacl, -1 as datconnlimit, " + "'pg_default' AS dattablespace " ++ ",NULL AS seclabel " + "FROM pg_database d " + "WHERE datallowconn ORDER BY 1"); + else +@@ -1210,18 +1230,20 @@ dumpCreateDB(PGconn *conn) + * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal + * with getting a NULL by not printing any OWNER clause. + */ +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "(select usename from pg_shadow where usesysid=datdba), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " + "'f' as datistemplate, " + "'' as datacl, -1 as datconnlimit, " + "'pg_default' AS dattablespace " ++ ",NULL AS seclabel " + "FROM pg_database d " + "ORDER BY 1"); + } + ++ res = PQexec(conn, buf->data); ++ + for (i = 0; i < PQntuples(res); i++) + { + char *dbname = PQgetvalue(res, i, 0); +@@ -1234,6 +1256,7 @@ dumpCreateDB(PGconn *conn) + char *dbacl = PQgetvalue(res, i, 7); + char *dbconnlimit = PQgetvalue(res, i, 8); + char *dbtablespace = PQgetvalue(res, i, 9); ++ char *dbseclabel = PQgetvalue(res, i, 10); + char *fdbname; + + fdbname = strdup(fmtId(dbname)); +@@ -1309,6 +1332,9 @@ dumpCreateDB(PGconn *conn) + appendPQExpBuffer(buf, ";\n"); + } + } ++ if (security_label && strlen(dbseclabel) > 0) ++ appendPQExpBuffer(buf, "ALTER DATABASE %s SECURITY LABEL TO '%s';\n", ++ dbname, dbseclabel); + + if (!skip_acls && + !buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner, +diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c +index b0bcbc2..cdb2592 100644 +--- a/src/bin/pg_dump/pg_restore.c ++++ b/src/bin/pg_dump/pg_restore.c +@@ -76,6 +76,7 @@ main(int argc, char **argv) + static int no_data_for_failed_tables = 0; + static int outputNoTablespaces = 0; + static int use_setsessauth = 0; ++ static int no_security_label = 0; + + struct option cmdopts[] = { + {"clean", 0, NULL, 'c'}, +@@ -116,6 +117,7 @@ main(int argc, char **argv) + {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, + {"role", required_argument, NULL, 2}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"no-security-label", no_argument, &no_security_label, 1}, + + {NULL, 0, NULL, 0} + }; +@@ -262,6 +264,8 @@ main(int argc, char **argv) + outputNoTablespaces = 1; + else if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; ++ else if (strcmp(optarg, "no-security-label") == 0) ++ no_security_label = 1; + else + { + fprintf(stderr, +@@ -326,6 +330,7 @@ main(int argc, char **argv) + opts->noDataForFailedTables = no_data_for_failed_tables; + opts->noTablespace = outputNoTablespaces; + opts->use_setsessauth = use_setsessauth; ++ opts->noSecLabel = no_security_label; + + if (opts->formatName) + { +@@ -437,6 +442,7 @@ usage(const char *progname) + " ALTER OWNER commands to set ownership\n")); + printf(_(" -1, --single-transaction\n" + " restore as a single transaction\n")); ++ printf(_(" --no-security-label skip restoration of security labels\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); +diff --git a/src/include/access/htup.h b/src/include/access/htup.h +index d87e888..270ff67 100644 +--- a/src/include/access/htup.h ++++ b/src/include/access/htup.h +@@ -163,7 +163,7 @@ typedef HeapTupleHeaderData *HeapTupleHeader; + #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ + #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ + #define HEAP_HASOID 0x0008 /* has an object-id field */ +-/* bit 0x0010 is available */ ++#define HEAP_HASSECID 0x0010 /* has an security-id field */ + #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ + #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ + #define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */ +@@ -292,6 +292,9 @@ do { \ + (tup)->t_choice.t_datum.datum_typmod = (typmod) \ + ) + ++#define HeapTupleHeaderHasOid(tup) \ ++ ((tup)->t_infomask & HEAP_HASOID) ++ + #define HeapTupleHeaderGetOid(tup) \ + ( \ + ((tup)->t_infomask & HEAP_HASOID) ? \ +@@ -351,6 +354,25 @@ do { \ + (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ + ) + ++#define HeapTupleHeaderHasSecid(tup) \ ++ ((tup)->t_infomask & HEAP_HASSECID) ++ ++#define HeapTupleHeaderGetSecid(tup) \ ++ ( \ ++ HeapTupleHeaderHasSecid(tup) \ ++ ? (*(Oid *)((char *)(tup) + (tup)->t_hoff \ ++ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ ++ - sizeof(Oid))) \ ++ : InvalidOid \ ++ ) ++ ++#define HeapTupleHeaderSetSecid(tup, secid) \ ++ do { \ ++ Assert(HeapTupleHeaderHasSecid(tup)); \ ++ *((Oid *)((char *)(tup) + (tup)->t_hoff \ ++ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ ++ - sizeof(Oid))) = (secid); \ ++ } while(0) + + /* + * BITMAPLEN(NATTS) - +@@ -545,12 +567,23 @@ typedef HeapTupleData *HeapTuple; + #define HeapTupleClearHeapOnly(tuple) \ + HeapTupleHeaderClearHeapOnly((tuple)->t_data) + ++#define HeapTupleHasOid(tuple) \ ++ HeapTupleHeaderHasOid((tuple)->t_data) ++ + #define HeapTupleGetOid(tuple) \ + HeapTupleHeaderGetOid((tuple)->t_data) + + #define HeapTupleSetOid(tuple, oid) \ + HeapTupleHeaderSetOid((tuple)->t_data, (oid)) + ++#define HeapTupleHasSecid(tuple) \ ++ HeapTupleHeaderHasSecid((tuple)->t_data) ++ ++#define HeapTupleGetSecid(tuple) \ ++ HeapTupleHeaderGetSecid((tuple)->t_data) ++ ++#define HeapTupleSetSecid(tuple, secid) \ ++ HeapTupleHeaderSetSecid((tuple)->t_data, (secid)) + + /* + * WAL record definitions for heapam.c's WAL operations +diff --git a/src/include/access/sysattr.h b/src/include/access/sysattr.h +index 59cd2cd..ad5903b 100644 +--- a/src/include/access/sysattr.h ++++ b/src/include/access/sysattr.h +@@ -25,7 +25,8 @@ + #define MaxTransactionIdAttributeNumber (-5) + #define MaxCommandIdAttributeNumber (-6) + #define TableOidAttributeNumber (-7) +-#define FirstLowInvalidHeapAttributeNumber (-8) ++#define SecurityLabelAttributeNumber (-8) ++#define FirstLowInvalidHeapAttributeNumber (-9) + + + #endif /* SYSATTR_H */ +diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h +index 53e0be6..dea713b 100644 +--- a/src/include/access/tupdesc.h ++++ b/src/include/access/tupdesc.h +@@ -75,13 +75,14 @@ typedef struct tupleDesc + Oid tdtypeid; /* composite type ID for tuple type */ + int32 tdtypmod; /* typmod for tuple type */ + bool tdhasoid; /* tuple has oid attribute in its header */ ++ bool tdhassecid; /* tuple has security id in its header */ + int tdrefcount; /* reference count, or -1 if not counting */ + } *TupleDesc; + + +-extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid); ++extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid, bool hassecid); + +-extern TupleDesc CreateTupleDesc(int natts, bool hasoid, ++extern TupleDesc CreateTupleDesc(int natts, bool hasoid, bool hassecid, + Form_pg_attribute *attrs); + + extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc); +diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h +index 49a32e3..f088876 100644 +--- a/src/include/bootstrap/bootstrap.h ++++ b/src/include/bootstrap/bootstrap.h +@@ -24,6 +24,7 @@ typedef enum + BgWriterProcess, + WalWriterProcess, + WalReceiverProcess, ++ SecurityWorkerProcess, + + NUM_AUXPROCTYPES /* Must be last! */ + } AuxProcType; +diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h +index 8292273..e2a86e8 100644 +--- a/src/include/catalog/heap.h ++++ b/src/include/catalog/heap.h +@@ -61,7 +61,8 @@ extern Oid heap_create_with_catalog(const char *relname, + OnCommitAction oncommit, + Datum reloptions, + bool use_user_acl, +- bool allow_system_table_mods); ++ bool allow_system_table_mods, ++ Oid *secLabels); + + extern void heap_drop_with_catalog(Oid relid); + +@@ -75,13 +76,15 @@ extern List *heap_truncate_find_FKs(List *relationIds); + + extern void InsertPgAttributeTuple(Relation pg_attribute_rel, + Form_pg_attribute new_attribute, +- CatalogIndexState indstate); ++ CatalogIndexState indstate, ++ Oid securityId); + + extern void InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, + Datum relacl, +- Datum reloptions); ++ Datum reloptions, ++ Oid securityId); + + extern List *AddRelationNewConstraints(Relation rel, + List *newColDefaults, +@@ -106,10 +109,13 @@ extern void RemoveAttrDefaultById(Oid attrdefId); + extern void RemoveStatistics(Oid relid, AttrNumber attnum); + + extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno, +- bool relhasoids); ++ bool relhasoids, bool relhassecids); + + extern Form_pg_attribute SystemAttributeByName(const char *attname, +- bool relhasoids); ++ bool relhasoids, bool relhassecids); ++ ++extern bool SystemAttributeWritable(AttrNumber attno, ++ bool relhasoids, bool relhassecids); + + extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind, + bool allow_system_table_mods); +diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h +index 4f437fd..069256f 100644 +--- a/src/include/catalog/indexing.h ++++ b/src/include/catalog/indexing.h +@@ -255,6 +255,11 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index, 2703, on pg_type using btree(oid oid_ops + DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); + #define TypeNameNspIndexId 2704 + ++DECLARE_UNIQUE_INDEX(pg_seclabel_secid_index, 3401, on pg_seclabel using btree(secid oid_ops, datid oid_ops, relid oid_ops)); ++#define SecLabelSecidIndexId 3401 ++DECLARE_INDEX(pg_seclabel_label_index, 3402, on pg_seclabel using btree(datid oid_ops, relid oid_ops, label text_ops)); ++#define SecLabelLabelIndexId 3402 ++ + DECLARE_UNIQUE_INDEX(pg_foreign_data_wrapper_oid_index, 112, on pg_foreign_data_wrapper using btree(oid oid_ops)); + #define ForeignDataWrapperOidIndexId 112 + +diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h +index 5ea514d..0f07482 100644 +--- a/src/include/catalog/pg_class.h ++++ b/src/include/catalog/pg_class.h +@@ -60,6 +60,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO + */ + int2 relchecks; /* # of CHECK constraints for class */ + bool relhasoids; /* T if we generate OIDs for rows of rel */ ++ bool relhassecids; /* T if we generate SIDs for rows of rel */ + bool relhaspkey; /* has (or has had) PRIMARY KEY index */ + bool relhasexclusion; /* has (or has had) exclusion constraint */ + bool relhasrules; /* has (or has had) any rules */ +@@ -93,7 +94,7 @@ typedef FormData_pg_class *Form_pg_class; + * ---------------- + */ + +-#define Natts_pg_class 27 ++#define Natts_pg_class 28 + #define Anum_pg_class_relname 1 + #define Anum_pg_class_relnamespace 2 + #define Anum_pg_class_reltype 3 +@@ -113,14 +114,15 @@ typedef FormData_pg_class *Form_pg_class; + #define Anum_pg_class_relnatts 17 + #define Anum_pg_class_relchecks 18 + #define Anum_pg_class_relhasoids 19 +-#define Anum_pg_class_relhaspkey 20 +-#define Anum_pg_class_relhasexclusion 21 +-#define Anum_pg_class_relhasrules 22 +-#define Anum_pg_class_relhastriggers 23 +-#define Anum_pg_class_relhassubclass 24 +-#define Anum_pg_class_relfrozenxid 25 +-#define Anum_pg_class_relacl 26 +-#define Anum_pg_class_reloptions 27 ++#define Anum_pg_class_relhassecids 20 ++#define Anum_pg_class_relhaspkey 21 ++#define Anum_pg_class_relhasexclusion 22 ++#define Anum_pg_class_relhasrules 23 ++#define Anum_pg_class_relhastriggers 24 ++#define Anum_pg_class_relhassubclass 25 ++#define Anum_pg_class_relfrozenxid 26 ++#define Anum_pg_class_relacl 27 ++#define Anum_pg_class_reloptions 28 + + /* ---------------- + * initial contents of pg_class +@@ -132,13 +134,13 @@ typedef FormData_pg_class *Form_pg_class; + */ + + /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */ +-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ )); ++DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t t f f f f f 3 _null_ _null_ )); + DESCR(""); +-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ )); ++DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f t f f f f f 3 _null_ _null_ )); + DESCR(""); +-DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f f r 25 0 t f f f f f 3 _null_ _null_ )); ++DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f f r 25 0 t t f f f f f 3 _null_ _null_ )); + DESCR(""); +-DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f f r 27 0 t f f f f f 3 _null_ _null_ )); ++DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t t f f f f f 3 _null_ _null_ )); + DESCR(""); + + #define RELKIND_INDEX 'i' /* secondary index */ +diff --git a/src/include/catalog/pg_conversion_fn.h b/src/include/catalog/pg_conversion_fn.h +index d40dea6..add5fd9 100644 +--- a/src/include/catalog/pg_conversion_fn.h ++++ b/src/include/catalog/pg_conversion_fn.h +@@ -17,7 +17,7 @@ + extern Oid ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +- Oid conproc, bool def); ++ Oid conproc, bool def, Oid securityId); + extern void RemoveConversionById(Oid conversionOid); + extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); + +diff --git a/src/include/catalog/pg_largeobject.h b/src/include/catalog/pg_largeobject.h +index c4c4a26..6025f56 100644 +--- a/src/include/catalog/pg_largeobject.h ++++ b/src/include/catalog/pg_largeobject.h +@@ -51,9 +51,10 @@ typedef FormData_pg_largeobject *Form_pg_largeobject; + #define Anum_pg_largeobject_pageno 2 + #define Anum_pg_largeobject_data 3 + +-extern Oid LargeObjectCreate(Oid loid); ++extern Oid LargeObjectCreate(Oid loid, Oid securityId); + extern void LargeObjectDrop(Oid loid); + extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId); ++extern void LargeObjectAlterSecLabel(Oid loid, char *new_label); + extern bool LargeObjectExists(Oid loid); + + #endif /* PG_LARGEOBJECT_H */ +diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h +index 425f039..0f8043d 100644 +--- a/src/include/catalog/pg_namespace.h ++++ b/src/include/catalog/pg_namespace.h +@@ -77,6 +77,6 @@ DESCR("standard public schema"); + /* + * prototypes for functions in pg_namespace.c + */ +-extern Oid NamespaceCreate(const char *nspName, Oid ownerId); ++extern Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid secid); + + #endif /* PG_NAMESPACE_H */ +diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h +index f2751a4..d7e7db8 100644 +--- a/src/include/catalog/pg_proc.h ++++ b/src/include/catalog/pg_proc.h +@@ -3719,6 +3719,10 @@ DESCR("current user privilege on role by role name"); + DATA(insert OID = 2710 ( pg_has_role PGNSP PGUID 12 1 0 0 f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ pg_has_role_id _null_ _null_ _null_ )); + DESCR("current user privilege on role by role oid"); + ++/* SE-PostgreSQL related stuff */ ++DATA(insert OID = 3405 ( sepgsql_tuple_perms PGNSP PGUID 12 0 0 0 f f f t f v 4 0 16 "26 2249 23 16" _null_ _null_ _null_ _null_ sepgsql_tuple_perms _null_ _null_ _null_ )); ++DATA(insert OID = 3406 ( seclabel_to_secid PGNSP PGUID 12 0 0 0 f f f t f v 1 0 26 "2249" _null_ _null_ _null_ _null_ seclabel_to_secid _null_ _null_ _null_ )); ++ + DATA(insert OID = 1269 ( pg_column_size PGNSP PGUID 12 1 0 0 f f f t f s 1 0 23 "2276" _null_ _null_ _null_ _null_ pg_column_size _null_ _null_ _null_ )); + DESCR("bytes required to store the value, perhaps with compression"); + DATA(insert OID = 2322 ( pg_tablespace_size PGNSP PGUID 12 1 0 0 f f f t f v 1 0 20 "26" _null_ _null_ _null_ _null_ pg_tablespace_size_oid _null_ _null_ _null_ )); +diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h +index c886f81..f2351c1 100644 +--- a/src/include/catalog/pg_proc_fn.h ++++ b/src/include/catalog/pg_proc_fn.h +@@ -37,7 +37,8 @@ extern Oid ProcedureCreate(const char *procedureName, + List *parameterDefaults, + Datum proconfig, + float4 procost, +- float4 prorows); ++ float4 prorows, ++ Oid prosecid); + + extern bool function_parse_error_transpose(const char *prosrc); + +diff --git a/src/include/catalog/pg_seclabel.h b/src/include/catalog/pg_seclabel.h +new file mode 100644 +index 0000000..21b25de +--- /dev/null ++++ b/src/include/catalog/pg_seclabel.h +@@ -0,0 +1,79 @@ ++/* ++ * pg_seclabel.h ++ * Definition of the security label relation (pg_seclabel) ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#ifndef PG_SECLABEL_H ++#define PG_SECLABEL_H ++ ++#include "catalog/genbki.h" ++ ++#include "access/htup.h" ++#include "access/skey.h" ++#include "utils/relcache.h" ++ ++#define SecLabelRelationId 3400 ++ ++CATALOG(pg_seclabel,3400) BKI_SHARED_RELATION BKI_WITHOUT_OIDS ++{ ++ /* Identifier of the security label */ ++ Oid secid; ++ ++ /* OID of the database which referes the entry */ ++ Oid datid; ++ ++ /* OID of the table which refers the entry */ ++ Oid relid; ++ ++ /* Text representation of the security label */ ++ text label; ++} FormData_pg_seclabel; ++ ++/* ++ * Form_pg_seclabel corresponds to a pointer to a tuple with ++ * the format of pg_seclabel relation. ++ */ ++typedef FormData_pg_seclabel *Form_pg_seclabel; ++ ++/* ++ * Compiler constants for pg_seclabel ++ */ ++#define Natts_pg_seclabel 4 ++#define Anum_pg_seclabel_secid 1 ++#define Anum_pg_seclabel_datid 2 ++#define Anum_pg_seclabel_relid 3 ++#define Anum_pg_seclabel_label 4 ++ ++/* ++ * Functions to translate between security label and identifier ++ */ ++extern bool ignore_security_label_input; ++ ++extern bool seclabelCatalogHasSysAttr(Oid relOid); ++extern void seclabelPostBootstrap(void); ++extern void seclabelOnCreateDatabase(Oid src_datOid, Oid dst_datOid); ++extern void seclabelOnDropDatabase(Oid datOid); ++extern void seclabelOnDropTable(Oid relOid); ++ ++extern Oid *seclabelMakeRelationDefaults(TupleDesc tupdesc, List *supOids); ++extern Oid *seclabelMakeToastDefaults(TupleDesc tupdesc, Oid relOid); ++ ++extern Oid seclabelGetNewSecid(Relation rel, HeapTuple tuple); ++ ++extern Oid seclabelRawInput(Oid relOid, char *seclabel); ++extern char *seclabelRawOutput(Oid relOid, Oid secid); ++extern Oid seclabelTransInput(Oid relOid, char *seclabel); ++extern char *seclabelTransOutput(Oid relOid, Oid secid); ++ ++extern Oid seclabelMoveSecid(Oid dst_relid, Oid src_relid, Oid secid); ++extern bool seclabelCompareSecid(Oid relid1, Oid secid1, ++ Oid relid2, Oid secid2); ++ ++extern Datum seclabelSysattOutput(Oid relOid, HeapTuple tuple); ++ ++extern void seclabelRelationReclaim(Oid relOid); ++extern Datum seclabel_to_secid(PG_FUNCTION_ARGS); ++ ++#endif /* PG_SECLABEL_H */ +diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h +index 5cf90af..1ca2dd4 100644 +--- a/src/include/catalog/pg_type_fn.h ++++ b/src/include/catalog/pg_type_fn.h +@@ -50,7 +50,8 @@ extern Oid TypeCreate(Oid newTypeOid, + char storage, + int32 typeMod, + int32 typNDims, +- bool typeNotNull); ++ bool typeNotNull, ++ Oid securityId); + + extern void GenerateTypeDependencies(Oid typeNamespace, + Oid typeObjectId, +diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h +index 95f86d3..ef9cd9b 100644 +--- a/src/include/catalog/toasting.h ++++ b/src/include/catalog/toasting.h +@@ -61,5 +61,8 @@ DECLARE_TOAST(pg_shdescription, 2846, 2847); + DECLARE_TOAST(pg_db_role_setting, 2966, 2967); + #define PgDbRoleSettingToastTable 2966 + #define PgDbRoleSettingToastIndex 2967 ++DECLARE_TOAST(pg_seclabel, 3403, 3404); ++#define PgSecLabelToastTable 3403 ++#define PgSecLabelToastIndex 3404 + + #endif /* TOASTING_H */ +diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h +index b1e04fb..6cd19ba 100644 +--- a/src/include/commands/alter.h ++++ b/src/include/commands/alter.h +@@ -19,5 +19,6 @@ + extern void ExecRenameStmt(RenameStmt *stmt); + extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); + extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); ++extern void ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt); + + #endif /* ALTER_H */ +diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h +index 542fc27..874e07a 100644 +--- a/src/include/commands/dbcommands.h ++++ b/src/include/commands/dbcommands.h +@@ -58,6 +58,7 @@ extern void RenameDatabase(const char *oldname, const char *newname); + extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel); + extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt); + extern void AlterDatabaseOwner(const char *dbname, Oid newOwnerId); ++extern void AlterDatabaseSecLabel(const char *dbname, char *new_label); + + extern Oid get_database_oid(const char *dbname); + extern char *get_database_name(Oid dbid); +diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h +index e8dbe81..7fc4922 100644 +--- a/src/include/commands/defrem.h ++++ b/src/include/commands/defrem.h +@@ -60,6 +60,8 @@ extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType); + extern void RenameFunction(List *name, List *argtypes, const char *newname); + extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId); + extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId); ++extern void AlterFunctionSecLabel(List *name, List *argtypes, ++ bool isagg, char *new_label); + extern void AlterFunction(AlterFunctionStmt *stmt); + extern void CreateCast(CreateCastStmt *stmt); + extern void DropCast(DropCastStmt *stmt); +diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h +index c914bd5..edd5abd 100644 +--- a/src/include/commands/schemacmds.h ++++ b/src/include/commands/schemacmds.h +@@ -26,5 +26,6 @@ extern void RemoveSchemaById(Oid schemaOid); + extern void RenameSchema(const char *oldname, const char *newname); + extern void AlterSchemaOwner(const char *name, Oid newOwnerId); + extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId); ++extern void AlterSchemaSecLabel(const char *name, char *new_label); + + #endif /* SCHEMACMDS_H */ +diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h +index f9269cc..a531f45 100644 +--- a/src/include/commands/tablecmds.h ++++ b/src/include/commands/tablecmds.h +@@ -35,6 +35,13 @@ extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, + Oid oldNspOid, Oid newNspOid, + bool hasDependEntry); + ++extern void AlterRelationSecLabel(RangeVar *relation, const char *attname, ++ ObjectType objtype, char *new_label); ++extern void AlterRelationSecLabelInternal(Oid relOid, Oid securityId, ++ int expected_parents); ++extern void AlterAttributeSecLabelInternal(Oid relOid, const char *attname, ++ Oid securityId, int expected_parents); ++ + extern void CheckTableNotInUse(Relation rel, const char *stmt); + + extern void ExecuteTruncate(TruncateStmt *stmt); +diff --git a/src/include/commands/tablespace.h b/src/include/commands/tablespace.h +index cf005ee..e9e8a55 100644 +--- a/src/include/commands/tablespace.h ++++ b/src/include/commands/tablespace.h +@@ -44,6 +44,7 @@ extern void DropTableSpace(DropTableSpaceStmt *stmt); + extern void RenameTableSpace(const char *oldname, const char *newname); + extern void AlterTableSpaceOwner(const char *name, Oid newOwnerId); + extern void AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt); ++extern void AlterTableSpaceSecLabel(const char *tspaceName, char *newLabel); + + extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo); + +diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h +index 8e5b610..f8c5872 100644 +--- a/src/include/commands/typecmds.h ++++ b/src/include/commands/typecmds.h +@@ -43,5 +43,7 @@ extern void AlterTypeNamespace(List *names, const char *newschema); + extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, + bool isImplicitArray, + bool errorOnTableType); ++extern void AlterTypeSecLabel(List *name, char *new_label); ++extern void AlterTypeSecLabelInternal(Oid typeOid, Oid securityId); + + #endif /* TYPECMDS_H */ +diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h +index 820314c..7873b44 100644 +--- a/src/include/executor/executor.h ++++ b/src/include/executor/executor.h +@@ -131,8 +131,8 @@ extern TupleHashEntry FindTupleHashEntry(TupleHashTable hashtable, + /* + * prototypes from functions in execJunk.c + */ +-extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, +- TupleTableSlot *slot); ++extern JunkFilter *ExecInitJunkFilter(List *targetList, ++ bool hasoid, bool hassecid, TupleTableSlot *slot); + extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, + TupleDesc cleanTupType, + TupleTableSlot *slot); +@@ -164,6 +164,7 @@ extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, + int instrument_options); + extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); + extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); ++extern bool ExecContextForcesSecids(PlanState *planstate, bool *hassecid); + extern void ExecConstraints(ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, EState *estate); + extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate, +@@ -234,8 +235,8 @@ extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate); + extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); + extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, + TupleDesc tupType); +-extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); +-extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); ++extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid); ++extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid); + extern TupleDesc ExecTypeFromExprList(List *exprList); + extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); + +diff --git a/src/include/fmgr.h b/src/include/fmgr.h +index d1a0dc1..e9de809 100644 +--- a/src/include/fmgr.h ++++ b/src/include/fmgr.h +@@ -51,6 +51,7 @@ typedef struct FmgrInfo + bool fn_retset; /* function returns a set */ + unsigned char fn_stats; /* collect stats if track_functions > this */ + void *fn_extra; /* extra space for use by handler */ ++ char *fn_seclabel; /* function is trusted procedure, or NULL */ + MemoryContext fn_mcxt; /* memory context to store fn_extra in */ + fmNodePtr fn_expr; /* expression parse tree for call, or NULL */ + } FmgrInfo; +diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h +index 1b5e476..37ffdfa 100644 +--- a/src/include/nodes/nodes.h ++++ b/src/include/nodes/nodes.h +@@ -346,6 +346,7 @@ typedef enum NodeTag + T_AlterUserMappingStmt, + T_DropUserMappingStmt, + T_AlterTableSpaceOptionsStmt, ++ T_AlterSecLabelStmt, + + /* + * TAGS FOR PARSE TREE NODES (parsenodes.h) +diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h +index b591073..53609e8 100644 +--- a/src/include/nodes/parsenodes.h ++++ b/src/include/nodes/parsenodes.h +@@ -731,6 +731,7 @@ typedef struct RangeTblEntry + Oid checkAsUser; /* if valid, check access as this role */ + Bitmapset *selectedCols; /* columns needing SELECT permission */ + Bitmapset *modifiedCols; /* columns needing INSERT/UPDATE permission */ ++ uint32 rowlvPerms; /* permissions for row-level access controls */ + } RangeTblEntry; + + /* +@@ -1134,6 +1135,8 @@ typedef enum AlterTableType + AT_DropCluster, /* SET WITHOUT CLUSTER */ + AT_AddOids, /* SET WITH OIDS */ + AT_DropOids, /* SET WITHOUT OIDS */ ++ AT_AddSecLabel, /* SET WITH SECURITY LABEL */ ++ AT_DropSecLabel, /* SET WITHOUT SECURITY LABEL */ + AT_SetTableSpace, /* SET TABLESPACE */ + AT_SetRelOptions, /* SET (...) -- AM specific parameters */ + AT_ResetRelOptions, /* RESET (...) -- AM specific parameters */ +@@ -2073,6 +2076,20 @@ typedef struct AlterOwnerStmt + char *newowner; /* the new owner */ + } AlterOwnerStmt; + ++/* ---------------------- ++ * Alter Security Label Statement ++ * ---------------------- ++ */ ++typedef struct AlterSecLabelStmt ++{ ++ NodeTag type; ++ ObjectType objectType; /* OBJECT_TABLE, OBJECT_TYPE, etc */ ++ RangeVar *relation; /* in case it's a table */ ++ List *object; /* in case it's some other object */ ++ List *objarg; /* argument types, if applicable */ ++ char *addname; /* additional name if needed */ ++ Value *secLabel; /* the new security label */ ++} AlterSecLabelStmt; + + /* ---------------------- + * Create Rule Statement +diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h +index 49d4b6c..13c88a1 100644 +--- a/src/include/parser/kwlist.h ++++ b/src/include/parser/kwlist.h +@@ -208,6 +208,7 @@ PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD) + PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD) + PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD) + PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD) ++PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD) + PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD) + PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD) + PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD) +diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in +index 684aed4..2080c95 100644 +--- a/src/include/pg_config.h.in ++++ b/src/include/pg_config.h.in +@@ -412,6 +412,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_SECURITY_PAM_APPL_H + ++/* Define to 1 if you enable SELinux support */ ++#undef HAVE_SELINUX ++ + /* Define to 1 if you have the `setproctitle' function. */ + #undef HAVE_SETPROCTITLE + +diff --git a/src/include/sepgsql/hooks.h b/src/include/sepgsql/hooks.h +new file mode 100644 +index 0000000..4e983f8 +--- /dev/null ++++ b/src/include/sepgsql/hooks.h +@@ -0,0 +1,284 @@ ++/* ++ * sepgsql/hooks.h ++ * ++ * Header of SE-PostgreSQL Hooks ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#ifndef SEPGSQL_HOOKS_H ++#define SEPGSQL_HOOKS_H ++ ++#include "nodes/plannodes.h" ++#include "nodes/relation.h" ++#include "storage/fd.h" ++#include "utils/acl.h" ++#include "utils/rel.h" ++#include "utils/snapshot.h" ++ ++/* ++ * misc.c ++ */ ++extern char *sepgsql_get_client_label(void); ++extern char *sepgsql_set_client_label(char *new_label); ++extern void sepgsql_post_bootstraping(void); ++extern void sepgsql_initialize(void); ++extern bool sepgsql_worker_needed(void); ++extern void sepgsql_worker_main(void); ++ ++/* ++ * database.c ++ */ ++extern Oid sepgsql_database_create(const char *datName, Oid templateOid); ++extern void sepgsql_database_alter(Oid databaseOid); ++extern Oid sepgsql_database_relabel(Oid databaseOid, char *new_label); ++extern void sepgsql_database_drop(Oid databaseOid, bool cascade); ++extern void sepgsql_database_grant(Oid databaseOid); ++extern void sepgsql_database_comment(Oid databaseOid); ++extern void sepgsql_database_connect(Oid databaseOid); ++extern void sepgsql_database_reindex(Oid databaseOid); ++extern void sepgsql_database_getattr(Oid databaseOid); ++ ++/* ++ * schema.c ++ */ ++extern Oid sepgsql_schema_create(const char *nspName, bool is_temp); ++extern void sepgsql_schema_alter(Oid namespaceOid); ++extern Oid sepgsql_schema_relabel(Oid namespaceOid, char *new_label); ++extern void sepgsql_schema_drop(Oid namespaceOid, bool cascade); ++extern void sepgsql_schema_grant(Oid namespaceOid); ++extern bool sepgsql_schema_search(Oid namespaceOid, bool abort); ++extern void sepgsql_schema_comment(Oid namespaceOid); ++ ++/* ++ * relation.c ++ */ ++extern bool sepgsql_relation_perms(Oid relOid, AclMode aclmask, ++ Bitmapset *selectedCols, ++ Bitmapset *modifiedCols, bool abort); ++extern Oid *sepgsql_relation_create(const char *relName, ++ char relkind, ++ TupleDesc tupDesc, ++ Oid namespaceId, ++ List *supOids, ++ bool createAs); ++extern void sepgsql_relation_alter(Oid relationOid); ++extern void sepgsql_relation_alter_schema(Oid relationOid, Oid newSchema); ++extern void sepgsql_relation_alter_rename(Oid relationOid, ++ const char *newName); ++extern void sepgsql_relation_alter_inherit(Oid childOid, Oid parentOid); ++extern Oid sepgsql_relation_relabel(Oid relationOid, char *new_label); ++extern void sepgsql_relation_drop(Oid relationOid, bool cascade); ++extern void sepgsql_relation_getattr(Oid relationOid); ++extern void sepgsql_relation_grant(Oid relationOid); ++extern void sepgsql_relation_comment(Oid relationOid); ++extern bool sepgsql_relation_cluster(Oid relationOid, bool abort); ++extern void sepgsql_relation_truncate(Relation rel); ++extern void sepgsql_relation_lock(Relation rel); ++extern void sepgsql_relation_reindex(Oid relOid); ++extern void sepgsql_view_replace(Oid viewOid); ++extern void sepgsql_index_create(Oid relationOid, Oid namespaceOid); ++extern void sepgsql_index_reindex(Oid indexOid); ++extern void sepgsql_sequence_get_value(Oid sequenceOid); ++extern void sepgsql_sequence_next_value(Oid sequenceOid); ++extern void sepgsql_sequence_set_value(Oid sequenceOid); ++extern void sepgsql_rule_create(Oid relationOid, ++ const char *ruleName); ++extern void sepgsql_rule_drop(Oid relationOid, ++ const char *ruleName, bool cascade); ++extern void sepgsql_rule_comment(Oid relationOid, const char *ruleName); ++extern void sepgsql_trigger_create(Oid relationOid, const char *triggerName, ++ Oid constrrelid, Oid funcOid); ++extern void sepgsql_trigger_alter(Oid relOid, const char *tgName); ++extern void sepgsql_trigger_drop(Oid relOid, const char *tgName, bool cascade); ++extern void sepgsql_trigger_comment(Oid relOid, const char *tgName); ++extern void sepgsql_constraint_comment(Oid relOid, const char *constName); ++ ++/* ++ * attribute.c ++ */ ++extern Oid sepgsql_attribute_create(Oid relOid, const char *attName); ++extern void sepgsql_attribute_alter(Oid relOid, const char *attName); ++extern Oid sepgsql_attribute_relabel(Oid relOid, const char *attName, ++ char *new_label); ++extern void sepgsql_attribute_drop(Oid relOid, const char *attName, bool cascade); ++extern void sepgsql_attribute_grant(Oid relOid, AttrNumber attnum); ++extern void sepgsql_attribute_comment(Oid relOid, AttrNumber attnum); ++ ++/* ++ * proc.c ++ */ ++extern Oid sepgsql_proc_create(const char *proName, Oid replaced, ++ Oid namespaceOid, Oid langageOid); ++extern void sepgsql_proc_alter(Oid procOid); ++extern void sepgsql_proc_alter_rename(Oid procOid, const char *newName); ++extern void sepgsql_proc_alter_schema(Oid procOid, Oid newSchema); ++extern Oid sepgsql_proc_relabel(Oid procOid, char *new_label); ++extern void sepgsql_proc_drop(Oid procOid, bool cascade); ++extern void sepgsql_proc_grant(Oid procOid); ++extern void sepgsql_proc_comment(Oid procOid); ++extern void sepgsql_proc_execute(Oid procOid); ++extern bool sepgsql_proc_be_inlined(HeapTuple protup); ++extern char *sepgsql_proc_domtrans(HeapTuple protup, MemoryContext mcxt); ++extern Oid sepgsql_aggregate_create(const char *aggName, Oid namespaceId, ++ Oid transFunc, Oid finalFunc); ++extern void sepgsql_aggregate_execute(Oid aggOid); ++ ++/* ++ * type.c ++ */ ++extern Oid sepgsql_type_create(const char *typeName, Oid replaced, ++ Oid namespaceId, char typeType, ++ Oid inputFunc, Oid outputFunc, ++ Oid recvFunc, Oid sendFunc, ++ Oid modinFunc, Oid modoutFunc, ++ Oid analyzeFunc); ++extern void sepgsql_type_alter(Oid typeOid); ++extern void sepgsql_type_alter_rename(Oid typeOid, const char *newName); ++extern void sepgsql_type_alter_schema(Oid typeOid, Oid newSchema); ++extern Oid sepgsql_type_relabel(Oid typeOid, char *newLabel); ++extern void sepgsql_type_drop(Oid typeOid, bool cascade); ++extern void sepgsql_type_comment(Oid typeOid); ++extern Oid sepgsql_cast_create(Oid sourceTypeOid, Oid targetTypeOid, ++ char castMethod, Oid castFuncOid); ++extern void sepgsql_cast_drop(Oid srcTypeOid, Oid dstTypeOid, bool cascade); ++extern void sepgsql_cast_comment(Oid srcTypeOid, Oid dstTypeOid); ++ ++/* ++ * tablespace.h ++ */ ++extern Oid sepgsql_tablespace_create(const char *tablespaceName); ++extern void sepgsql_tablespace_alter(Oid tablespaceOid); ++extern Oid sepgsql_tablespace_relabel(Oid tablespaceOid, char *newLabel); ++extern void sepgsql_tablespace_drop(Oid tablespaceOid, bool cascade); ++extern void sepgsql_tablespace_grant(Oid tablespaceOid); ++extern void sepgsql_tablespace_getattr(Oid tablespaceOid); ++extern void sepgsql_tablespace_comment(Oid tablespaceOid); ++ ++/* ++ * operator.h ++ */ ++extern Oid sepgsql_operator_create(const char *operName, Oid replaced, ++ Oid namespaceId, ++ Oid codeFn, Oid restrictFn, Oid joinFn, ++ Oid commutatorOp, Oid negatorOp); ++extern void sepgsql_operator_alter(Oid operOid); ++extern Oid sepgsql_operator_relabel(Oid operOid, char *newLabel); ++extern void sepgsql_operator_drop(Oid operOid, bool cascade); ++extern void sepgsql_operator_comment(Oid operOid); ++ ++extern Oid sepgsql_opclass_create(const char *opcName, Oid namespaceId, ++ Oid typeOid, Oid opfamilyOid, Oid storageOid); ++extern void sepgsql_opclass_alter(Oid opcOid); ++extern void sepgsql_opclass_alter_rename(Oid opcOid, const char *newName); ++extern void sepgsql_opclass_drop(Oid opcOid, bool cascade); ++extern void sepgsql_opclass_comment(Oid opcOid); ++ ++extern Oid sepgsql_opfamily_create(const char *opfName, ++ Oid namespaceId, Oid amOid); ++extern void sepgsql_opfamily_alter(Oid opfOid, bool isDrop, Oid amOid, ++ List *operators, List *procedures); ++extern void sepgsql_opfamily_alter_rename(Oid opfOid, const char *newName); ++extern void sepgsql_opfamily_alter_owner(Oid opfOid, Oid newOwner); ++extern void sepgsql_opfamily_drop(Oid opfOid, bool cascade); ++extern void sepgsql_opfamily_comment(Oid opfOid); ++ ++/* ++ * role.c ++ */ ++extern Oid sepgsql_role_create(const char *roleName); ++extern void sepgsql_role_alter(Oid roleOid); ++extern Oid sepgsql_role_relabel(Oid roleOid, char *newLabel); ++extern void sepgsql_role_drop(Oid roleOid, bool cascade); ++extern void sepgsql_role_grant(Oid roleOid, bool is_grant, List *memberIds); ++extern void sepgsql_role_comment(Oid roleOid); ++ ++/* ++ * blob.c ++ */ ++extern Oid sepgsql_largeobject_create(Oid loid); ++extern void sepgsql_largeobject_alter(Oid loid); ++extern Oid sepgsql_largeobject_relabel(Oid loid, char *newLabel); ++extern void sepgsql_largeobject_drop(Oid loid, bool cascade); ++extern void sepgsql_largeobject_read(Oid loid, Snapshot snapshot); ++extern void sepgsql_largeobject_write(Oid loid, Snapshot snapshot); ++extern Oid sepgsql_largeobject_import(Oid loid, const char *filename); ++extern void sepgsql_largeobject_export(Oid loid, Snapshot snapshot, ++ const char *filename); ++extern void sepgsql_largeobject_grant(Oid loid); ++extern void sepgsql_largeobject_comment(Oid loid); ++ ++/* ++ * conversion.c ++ */ ++extern Oid sepgsql_conversion_create(const char *convName, ++ Oid namespaceId, Oid conversionFunc); ++extern void sepgsql_conversion_alter(Oid convOid); ++extern void sepgsql_conversion_alter_rename(Oid convOid, const char *newName); ++extern void sepgsql_conversion_drop(Oid convOid, bool cascade); ++extern void sepgsql_conversion_comment(Oid convOid); ++ ++/* ++ * tsearch.c ++ */ ++extern Oid sepgsql_ts_config_create(const char *confName, Oid namespaceId); ++extern void sepgsql_ts_config_alter(Oid confOid); ++extern void sepgsql_ts_config_alter_rename(Oid confOid, const char *newName); ++extern void sepgsql_ts_config_drop(Oid confOid, bool cascade); ++extern void sepgsql_ts_config_comment(Oid confOid); ++extern Oid sepgsql_ts_dict_create(const char *dictName, Oid namespaceId); ++extern void sepgsql_ts_dict_alter(Oid dictOid); ++extern void sepgsql_ts_dict_alter_rename(Oid dictOid, const char *newName); ++extern void sepgsql_ts_dict_drop(Oid dictOid, bool cascade); ++extern void sepgsql_ts_dict_comment(Oid dictOid); ++extern Oid sepgsql_ts_parser_create(const char *parseName, Oid namespaceId, ++ Oid startFunc, Oid tokenFunc, Oid endFunc, ++ Oid headlineFunc, Oid lextypeFunc); ++extern void sepgsql_ts_parser_alter_rename(Oid parseOid, const char *newName); ++extern void sepgsql_ts_parser_drop(Oid parseOid, bool cascade); ++extern void sepgsql_ts_parser_comment(Oid parseOid); ++extern Oid sepgsql_ts_template_create(const char *templateName, ++ Oid namespaceId, ++ Oid initFunc, Oid lexizeFunc); ++extern void sepgsql_ts_template_alter_rename(Oid templateOid, ++ const char *newName); ++extern void sepgsql_ts_template_drop(Oid templateOid, bool cascade); ++extern void sepgsql_ts_template_comment(Oid templateOid); ++ ++/* ++ * fdw.c ++ */ ++extern Oid sepgsql_fdw_create(const char *fdwName, Oid validatorFunc); ++extern void sepgsql_fdw_alter(Oid fdwOid, Oid newValidator); ++extern void sepgsql_fdw_drop(Oid fdwOid, bool cascade); ++extern void sepgsql_fdw_grant(Oid fdwOid); ++ ++extern Oid sepgsql_fserver_create(const char *fservName, Oid fdwOid); ++extern void sepgsql_fserver_alter(Oid fservOid); ++extern void sepgsql_fserver_drop(Oid fservOid, bool cascade); ++extern void sepgsql_fserver_grant(Oid fservOid); ++ ++extern Oid sepgsql_user_mapping_create(Oid mappedRoleId, Oid fservOid); ++extern void sepgsql_user_mapping_alter(Oid umapOid); ++extern void sepgsql_user_mapping_drop(Oid umapOid, bool cascade); ++ ++/* ++ * row-level access controls ++ */ ++#define SEPGSQL_ROWLV_FILTER 1 ++#define SEPGSQL_ROWLV_ABORT 2 ++#define SEPGSQL_ROWLV_BYPASS 3 ++ ++extern int sepgsql_rowlv_get_mode(void); ++extern int sepgsql_rowlv_set_mode(int new_mode); ++extern void sepgsql_rowlv_add_policy(PlannerInfo *root, Scan *plan); ++extern uint32 sepgsql_rowlv_permissions(RangeTblEntry *rte); ++ ++extern void sepgsql_proxy_queries(List *queryList); ++ ++extern void sepgsql_tuple_insert(Relation rel, HeapTuple tuple); ++extern void sepgsql_tuple_update(Relation rel, ItemPointer otid, HeapTuple newtup); ++ ++extern Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS); ++ ++#endif /* SEPGSQL_HOOKS_H */ +diff --git a/src/include/sepgsql/sepgsql.h b/src/include/sepgsql/sepgsql.h +new file mode 100644 +index 0000000..ce65466 +--- /dev/null ++++ b/src/include/sepgsql/sepgsql.h +@@ -0,0 +1,298 @@ ++/* ++ * sepgsql/sepgsql.h ++ * ++ * Header of SE-PostgreSQL Internal ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#ifndef SEPGSQL_H ++#define SEPGSQL_H ++ ++#include "utils/snapshot.h" ++ ++/* GUC : sepostgresql */ ++extern int sepostgresql_mode; ++ ++#define SEPGSQL_MODE_DEFAULT 1 ++#define SEPGSQL_MODE_ENFORCING 2 ++#define SEPGSQL_MODE_PERMISSIVE 3 ++#define SEPGSQL_MODE_INTERNAL 4 ++#define SEPGSQL_MODE_DISABLED 5 ++ ++/* GUC: sepostgresql_mcstrans */ ++extern bool sepgsql_mcstrans; ++ ++/* GUC: sepostgresql_debug_audit */ ++extern bool sepgsql_debug_audit; ++ ++/* Objject classes and permissions internally used */ ++enum SepgsqlClasses ++{ ++ SEPG_CLASS_PROCESS = 0, ++ SEPG_CLASS_FILE, ++ SEPG_CLASS_DIR, ++ SEPG_CLASS_LNK_FILE, ++ SEPG_CLASS_CHR_FILE, ++ SEPG_CLASS_BLK_FILE, ++ SEPG_CLASS_SOCK_FILE, ++ SEPG_CLASS_FIFO_FILE, ++ SEPG_CLASS_DB_DATABASE, ++ SEPG_CLASS_DB_SCHEMA, ++ SEPG_CLASS_DB_TABLE, ++ SEPG_CLASS_DB_VIEW, ++ SEPG_CLASS_DB_SEQUENCE, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_CLASS_DB_COLUMN, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_CLASS_DB_LANGUAGE, ++ SEPG_CLASS_MAX, ++}; ++ ++#define SEPG_PROCESS__TRANSITION (1<<0) ++ ++#define SEPG_FILE__READ (1<<0) ++#define SEPG_FILE__WRITE (1<<1) ++#define SEPG_FILE__CREATE (1<<2) ++#define SEPG_FILE__GETATTR (1<<3) ++ ++#define SEPG_DIR__READ (SEPG_FILE__READ) ++#define SEPG_DIR__WRITE (SEPG_FILE__WRITE) ++#define SEPG_DIR__CREATE (SEPG_FILE__CREATE) ++#define SEPG_DIR__GETATTR (SEPG_FILE__GETATTR) ++ ++#define SEPG_LNK_FILE__READ (SEPG_FILE__READ) ++#define SEPG_LNK_FILE__WRITE (SEPG_FILE__WRITE) ++#define SEPG_LNK_FILE__CREATE (SEPG_FILE__CREATE) ++#define SEPG_LNK_FILE__GETATTR (SEPG_FILE__GETATTR) ++ ++#define SEPG_CHR_FILE__READ (SEPG_FILE__READ) ++#define SEPG_CHR_FILE__WRITE (SEPG_FILE__WRITE) ++#define SEPG_CHR_FILE__CREATE (SEPG_FILE__CREATE) ++#define SEPG_CHR_FILE__GETATTR (SEPG_FILE__GETATTR) ++ ++#define SEPG_BLK_FILE__READ (SEPG_FILE__READ) ++#define SEPG_BLK_FILE__WRITE (SEPG_FILE__WRITE) ++#define SEPG_BLK_FILE__CREATE (SEPG_FILE__CREATE) ++#define SEPG_BLK_FILE__GETATTR (SEPG_FILE__GETATTR) ++ ++#define SEPG_SOCK_FILE__READ (SEPG_FILE__READ) ++#define SEPG_SOCK_FILE__WRITE (SEPG_FILE__WRITE) ++#define SEPG_SOCK_FILE__CREATE (SEPG_FILE__CREATE) ++#define SEPG_SOCK_FILE__GETATTR (SEPG_FILE__GETATTR) ++ ++#define SEPG_FIFO_FILE__READ (SEPG_FILE__READ) ++#define SEPG_FIFO_FILE__WRITE (SEPG_FILE__WRITE) ++#define SEPG_FIFO_FILE__CREATE (SEPG_FILE__CREATE) ++#define SEPG_FIFO_FILE__GETATTR (SEPG_FILE__GETATTR) ++ ++#define SEPG_DB_DATABASE__CREATE (1<<0) ++#define SEPG_DB_DATABASE__DROP (1<<1) ++#define SEPG_DB_DATABASE__GETATTR (1<<2) ++#define SEPG_DB_DATABASE__SETATTR (1<<3) ++#define SEPG_DB_DATABASE__RELABELFROM (1<<4) ++#define SEPG_DB_DATABASE__RELABELTO (1<<5) ++#define SEPG_DB_DATABASE__ACCESS (1<<6) ++#define SEPG_DB_DATABASE__LOAD_MODULE (1<<7) ++ ++#define SEPG_DB_SCHEMA__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_SCHEMA__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_SCHEMA__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_SCHEMA__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_SCHEMA__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_SCHEMA__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_SCHEMA__SEARCH (1<<6) ++#define SEPG_DB_SCHEMA__ADD_NAME (1<<7) ++#define SEPG_DB_SCHEMA__REMOVE_NAME (1<<8) ++ ++#define SEPG_DB_TABLE__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_TABLE__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_TABLE__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_TABLE__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_TABLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_TABLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_TABLE__SELECT (1<<6) ++#define SEPG_DB_TABLE__UPDATE (1<<7) ++#define SEPG_DB_TABLE__INSERT (1<<8) ++#define SEPG_DB_TABLE__DELETE (1<<9) ++#define SEPG_DB_TABLE__LOCK (1<<10) ++#define SEPG_DB_TABLE__INDEXON (1<<11) ++ ++#define SEPG_DB_SEQUENCE__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_SEQUENCE__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_SEQUENCE__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_SEQUENCE__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_SEQUENCE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_SEQUENCE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_SEQUENCE__GET_VALUE (1<<6) ++#define SEPG_DB_SEQUENCE__NEXT_VALUE (1<<7) ++#define SEPG_DB_SEQUENCE__SET_VALUE (1<<8) ++ ++#define SEPG_DB_VIEW__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_VIEW__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_VIEW__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_VIEW__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_VIEW__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_VIEW__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_VIEW__EXPAND (1<<6) ++ ++#define SEPG_DB_PROCEDURE__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_PROCEDURE__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_PROCEDURE__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_PROCEDURE__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_PROCEDURE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_PROCEDURE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_PROCEDURE__EXECUTE (1<<6) ++#define SEPG_DB_PROCEDURE__ENTRYPOINT (1<<7) ++#define SEPG_DB_PROCEDURE__INSTALL (1<<8) ++ ++#define SEPG_DB_COLUMN__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_COLUMN__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_COLUMN__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_COLUMN__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_COLUMN__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_COLUMN__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_COLUMN__SELECT (1<<6) ++#define SEPG_DB_COLUMN__UPDATE (1<<7) ++#define SEPG_DB_COLUMN__INSERT (1<<8) ++ ++#define SEPG_DB_TUPLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_TUPLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_TUPLE__SELECT (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_TUPLE__UPDATE (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_TUPLE__INSERT (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_TUPLE__DELETE (SEPG_DB_DATABASE__DROP) ++ ++#define SEPG_DB_BLOB__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_BLOB__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_BLOB__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_BLOB__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_BLOB__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_BLOB__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_BLOB__READ (1<<6) ++#define SEPG_DB_BLOB__WRITE (1<<7) ++#define SEPG_DB_BLOB__IMPORT (1<<8) ++#define SEPG_DB_BLOB__EXPORT (1<<9) ++ ++#define SEPG_DB_LANGUAGE__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_LANGUAGE__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_LANGUAGE__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_LANGUAGE__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_LANGUAGE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_LANGUAGE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_LANGUAGE__IMPLEMENTE (1<<6) ++#define SEPG_DB_LANGUAGE__EXECUTE (1<<7) ++ ++/* ++ * sepgsql_sid_t : alternative representation of security context ++ */ ++typedef struct { ++ Oid relid; ++ Oid secid; ++} sepgsql_sid_t; ++ ++struct av_decision; ++ ++/* ++ * selinux.c ++ */ ++extern bool sepgsql_is_enabled(void); ++extern bool sepgsql_get_enforce(void); ++extern const char *sepgsql_show_mode(void); ++ ++extern Size sepgsql_shmem_size(void); ++ ++extern void sepgsql_audit_log(bool denied, ++ char *scontext, ++ char *tcontext, ++ uint16 tclass, ++ uint32 audited, ++ const char *audit_name); ++extern void sepgsql_compute_avd(char *scontext, ++ char *tcontext, ++ uint16 tclass, ++ struct av_decision *avd); ++extern bool sepgsql_compute_perms(char *scontext, ++ char *tcontext, ++ uint16 tclass, ++ uint32 required, ++ const char *audit_name, ++ bool abort); ++extern char *sepgsql_compute_create(char *scontext, ++ char *tcontext, ++ uint16 tclass); ++extern bool sepgsql_client_perms(sepgsql_sid_t tsid, ++ uint16 tclass, ++ uint32 required, ++ const char *audit_name, ++ bool abort); ++extern sepgsql_sid_t sepgsql_client_create_secid(sepgsql_sid_t tsid, ++ uint16 tclass, ++ Oid nrelid); ++extern char *sepgsql_client_create_label(sepgsql_sid_t tsid, ++ uint16 tclass); ++extern void sepgsql_avc_worker_main(void); ++ ++/* ++ * avc.c ++ */ ++extern Size sepgsql_shmem_size(void); ++extern void sepgsql_avc_init(void); ++extern void sepgsql_avc_switch(const char *scontext); ++ ++ ++/* ++ * label.c ++ */ ++extern sepgsql_sid_t sepgsql_move_secid(Oid dst_relid, sepgsql_sid_t ssid); ++ ++extern sepgsql_sid_t sepgsql_get_default_database_secid(Oid templateOid); ++extern sepgsql_sid_t sepgsql_get_default_schema_secid(Oid databaseOid); ++extern sepgsql_sid_t sepgsql_get_default_table_secid(Oid namespaceOid); ++extern sepgsql_sid_t sepgsql_get_default_sequence_secid(Oid namespaceOid); ++extern sepgsql_sid_t sepgsql_get_default_view_secid(Oid namespaceOid); ++extern sepgsql_sid_t sepgsql_get_default_proc_secid(Oid namespaceOid); ++extern sepgsql_sid_t sepgsql_get_default_column_secid(Oid tableOid); ++extern sepgsql_sid_t sepgsql_get_default_tuple_secid(Oid tableOid); ++extern sepgsql_sid_t sepgsql_get_default_blob_secid(Oid databaseOid); ++extern Oid sepgsql_get_default_secid(Relation rel, HeapTuple tuple); ++ ++extern void sepgsql_initial_labeling(void); ++ ++extern char *sepgsql_mcstrans_out(char *label); ++extern char *sepgsql_mcstrans_in(char *label); ++extern char *sepgsql_rawlabel_out(char *label); ++extern char *sepgsql_rawlabel_in(char *label); ++ ++/* ++ * sepgsql_(object)_common ++ */ ++extern bool sepgsql_database_common(Oid datOid, uint32 required, bool abort); ++extern bool sepgsql_schema_common(Oid nspOid, uint32 required, bool abort); ++extern bool sepgsql_relation_common(Oid relOid, uint32 required, bool abort); ++extern bool sepgsql_attribute_common(Oid relOid, AttrNumber attno, ++ uint32 required, bool abort); ++extern bool sepgsql_proc_common(Oid procOid, uint32 required, bool abort); ++extern bool sepgsql_type_common(Oid typeOid, uint32 required, bool abort); ++extern bool sepgsql_cast_common(Oid srcTypeOid, Oid dstTypeOid, ++ uint32 required, bool abort); ++extern bool sepgsql_tablespace_common(Oid tspaceOid, uint32 required, bool abort); ++extern bool sepgsql_operator_common(Oid operOid, uint32 required, bool abort); ++extern bool sepgsql_opclass_common(Oid opcOid, uint32 required, bool abort); ++extern bool sepgsql_opfamily_common(Oid opfOid, uint32 required, bool abort); ++extern bool sepgsql_role_common(Oid roleOid, uint32 required, bool abort); ++extern bool sepgsql_largeobejct_common(Oid loid, Snapshot snapshot, ++ uint32 required, bool abort); ++extern bool sepgsql_conversion_common(Oid convOid, uint32 required, bool abort); ++extern bool sepgsql_largeobject_common(Oid loid, Snapshot snapshot, ++ uint32 required, bool abort); ++extern bool sepgsql_ts_config_common(Oid confOid, uint32 required, bool abort); ++extern bool sepgsql_ts_dict_common(Oid dictOid, uint32 required, bool abort); ++extern bool sepgsql_ts_parser_common(Oid parseOid, uint32 required, bool abort); ++extern bool sepgsql_ts_template_common(Oid templateOid, uint32 required, bool abort); ++extern bool sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort); ++extern bool sepgsql_fserver_common(Oid fservOid, uint32 required, bool abort); ++extern bool sepgsql_user_mapping_common(Oid umapOid, uint32 required, bool abort); ++ ++#endif /* SEPGSQL_H */ +diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h +index 43a61f3..6385b5c 100644 +--- a/src/include/storage/large_object.h ++++ b/src/include/storage/large_object.h +@@ -70,7 +70,7 @@ typedef struct LargeObjectDesc + + /* inversion stuff in inv_api.c */ + extern void close_lo_relation(bool isCommit); +-extern Oid inv_create(Oid lobjId); ++extern Oid inv_create(Oid lobjId, Oid securityId); + extern LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt); + extern void inv_close(LargeObjectDesc *obj_desc); + extern int inv_drop(Oid lobjId); +diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h +index 4eece8b..4176eac 100644 +--- a/src/include/storage/lwlock.h ++++ b/src/include/storage/lwlock.h +@@ -70,6 +70,7 @@ typedef enum LWLockId + RelationMappingLock, + AsyncCtlLock, + AsyncQueueLock, ++ SepgsqlAvcLock, + /* Individual lock IDs end here */ + FirstBufMappingLock, + FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, +diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h +index c22c65a..5b94283 100644 +--- a/src/include/utils/guc.h ++++ b/src/include/utils/guc.h +@@ -166,6 +166,7 @@ extern bool log_btree_build_stats; + + extern PGDLLIMPORT bool check_function_bodies; + extern bool default_with_oids; ++extern bool default_with_secids; + extern bool SQL_inheritance; + + extern int log_min_error_statement; +diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h +index 2f19e5c..6744782 100644 +--- a/src/include/utils/syscache.h ++++ b/src/include/utils/syscache.h +@@ -101,6 +101,8 @@ extern bool SearchSysCacheExists(int cacheId, + Datum key1, Datum key2, Datum key3, Datum key4); + extern Oid GetSysCacheOid(int cacheId, + Datum key1, Datum key2, Datum key3, Datum key4); ++extern Oid GetSysCacheSecid(int cacheId, ++ Datum key1, Datum key2, Datum key3, Datum key4); + + extern HeapTuple SearchSysCacheAttName(Oid relid, const char *attname); + extern HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname); +@@ -154,6 +156,15 @@ extern struct catclist *SearchSysCacheList(int cacheId, int nkeys, + #define GetSysCacheOid4(cacheId, key1, key2, key3, key4) \ + GetSysCacheOid(cacheId, key1, key2, key3, key4) + ++#define GetSysCacheSecid1(cacheId, key1) \ ++ GetSysCacheSecid(cacheId, key1, 0, 0, 0) ++#define GetSysCacheSecid2(cacheId, key1, key2) \ ++ GetSysCacheSecid(cacheId, key1, key2, 0, 0) ++#define GetSysCacheSecid3(cacheId, key1, key2, key3) \ ++ GetSysCacheSecid(cacheId, key1, key2, key3, 0) ++#define GetSysCacheSecid4(cacheId, key1, key2, key3, key4) \ ++ GetSysCacheSecid(cacheId, key1, key2, key3, key4) ++ + #define SearchSysCacheList1(cacheId, key1) \ + SearchSysCacheList(cacheId, 1, key1, 0, 0, 0) + #define SearchSysCacheList2(cacheId, key1, key2) \ +diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c +index 656ea73..2ce4e94 100644 +--- a/src/pl/plpgsql/src/pl_comp.c ++++ b/src/pl/plpgsql/src/pl_comp.c +@@ -1936,7 +1936,7 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars) + + row = palloc0(sizeof(PLpgSQL_row)); + row->dtype = PLPGSQL_DTYPE_ROW; +- row->rowtupdesc = CreateTemplateTupleDesc(numvars, false); ++ row->rowtupdesc = CreateTemplateTupleDesc(numvars, false, false); + row->nfields = numvars; + row->fieldnames = palloc(numvars * sizeof(char *)); + row->varnos = palloc(numvars * sizeof(int)); +diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out +index 1d9e110..9596b0b 100644 +--- a/src/test/regress/expected/sanity_check.out ++++ b/src/test/regress/expected/sanity_check.out +@@ -114,6 +114,7 @@ SELECT relname, relhasindex + pg_pltemplate | t + pg_proc | t + pg_rewrite | t ++ pg_seclabel | t + pg_shdepend | t + pg_shdescription | t + pg_statistic | t +@@ -153,7 +154,7 @@ SELECT relname, relhasindex + timetz_tbl | f + tinterval_tbl | f + varchar_tbl | f +-(142 rows) ++(143 rows) + + -- + -- another sanity check: every system catalog that has OIDs should have diff --git a/sepostgresql-fedora-prefix.patch b/sepostgresql-fedora-prefix.patch index a9dd24b..98cd3b2 100644 --- a/sepostgresql-fedora-prefix.patch +++ b/sepostgresql-fedora-prefix.patch @@ -1,8 +1,8 @@ -Index: sepgsql/src/Makefile.global.in -=================================================================== ---- sepgsql/src/Makefile.global.in (revision 2237) -+++ sepgsql/src/Makefile.global.in (working copy) -@@ -75,14 +75,14 @@ +diff --git a/src/Makefile.global.in b/src/Makefile.global.in +index 0e3bed5..aee6064 100644 +--- a/src/Makefile.global.in ++++ b/src/Makefile.global.in +@@ -74,14 +74,14 @@ bindir := @bindir@ datadir := @datadir@ ifeq "$(findstring pgsql, $(datadir))" "" ifeq "$(findstring postgres, $(datadir))" "" @@ -19,7 +19,7 @@ Index: sepgsql/src/Makefile.global.in endif endif -@@ -91,7 +91,7 @@ +@@ -90,7 +90,7 @@ libdir := @libdir@ pkglibdir = $(libdir) ifeq "$(findstring pgsql, $(pkglibdir))" "" ifeq "$(findstring postgres, $(pkglibdir))" "" @@ -28,7 +28,7 @@ Index: sepgsql/src/Makefile.global.in endif endif -@@ -100,7 +100,7 @@ +@@ -99,7 +99,7 @@ includedir := @includedir@ pkgincludedir = $(includedir) ifeq "$(findstring pgsql, $(pkgincludedir))" "" ifeq "$(findstring postgres, $(pkgincludedir))" "" @@ -37,7 +37,7 @@ Index: sepgsql/src/Makefile.global.in endif endif -@@ -109,7 +109,7 @@ +@@ -108,7 +108,7 @@ mandir := @mandir@ docdir := @docdir@ ifeq "$(findstring pgsql, $(docdir))" "" ifeq "$(findstring postgres, $(docdir))" "" @@ -46,37 +46,55 @@ Index: sepgsql/src/Makefile.global.in endif endif -Index: sepgsql/src/bin/pg_ctl/pg_ctl.c -=================================================================== ---- sepgsql/src/bin/pg_ctl/pg_ctl.c (revision 2237) -+++ sepgsql/src/bin/pg_ctl/pg_ctl.c (working copy) -@@ -643,7 +643,7 @@ - - postmaster_path = pg_malloc(MAXPGPATH); - -- if ((ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR, -+ if ((ret = find_other_exec(argv0, "sepostgres", PG_BACKEND_VERSIONSTR, - postmaster_path)) < 0) - { - char full_path[MAXPGPATH]; -Index: sepgsql/src/bin/initdb/initdb.c -=================================================================== ---- sepgsql/src/bin/initdb/initdb.c (revision 2237) -+++ sepgsql/src/bin/initdb/initdb.c (working copy) -@@ -2763,7 +2763,7 @@ - sprintf(pgdenv, "PGDATA=%s", pg_data); - putenv(pgdenv); +diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c +index 497bdf0..54908f8 100644 +--- a/src/bin/initdb/initdb.c ++++ b/src/bin/initdb/initdb.c +@@ -2722,7 +2722,7 @@ main(int argc, char *argv[]) + */ + putenv("TZ=GMT"); - if ((ret = find_other_exec(argv[0], "postgres", PG_BACKEND_VERSIONSTR, + if ((ret = find_other_exec(argv[0], "sepostgres", PG_BACKEND_VERSIONSTR, backend_exec)) < 0) { char full_path[MAXPGPATH]; -Index: sepgsql/src/bin/pg_dump/pg_dumpall.c -=================================================================== ---- sepgsql/src/bin/pg_dump/pg_dumpall.c (revision 2237) -+++ sepgsql/src/bin/pg_dump/pg_dumpall.c (working copy) -@@ -157,7 +157,7 @@ +diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c +index 814ce97..2550e57 100644 +--- a/src/bin/pg_ctl/pg_ctl.c ++++ b/src/bin/pg_ctl/pg_ctl.c +@@ -654,7 +654,7 @@ do_init(void) + char cmd[MAXPGPATH]; + + if (exec_path == NULL) +- exec_path = find_other_exec_or_die(argv0, "initdb", "initdb (PostgreSQL) " PG_VERSION "\n"); ++ exec_path = find_other_exec_or_die(argv0, "initdb.sepgsql", "initdb (PostgreSQL) " PG_VERSION "\n"); + + if (pgdata_opt == NULL) + pgdata_opt = ""; +@@ -699,7 +699,7 @@ do_start(void) + pgdata_opt = ""; + + if (exec_path == NULL) +- exec_path = find_other_exec_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR); ++ exec_path = find_other_exec_or_die(argv0, "sepostgres", PG_BACKEND_VERSIONSTR); + + #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE) + if (allow_core_files) +@@ -1069,7 +1069,7 @@ pgwin32_CommandLine(bool registration) + } + else + { +- ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR, ++ ret = find_other_exec(argv0, "sepostgres", PG_BACKEND_VERSIONSTR, + cmdLine); + if (ret != 0) + { +diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c +index 83f1678..a0a9444 100644 +--- a/src/bin/pg_dump/pg_dumpall.c ++++ b/src/bin/pg_dump/pg_dumpall.c +@@ -156,7 +156,7 @@ main(int argc, char *argv[]) } } diff --git a/sepostgresql.init b/sepostgresql.init index 866aaba..1a2cbb6 100644 --- a/sepostgresql.init +++ b/sepostgresql.init @@ -7,7 +7,7 @@ # pidfile: /var/run/postmaster.pid #--------------------------------------------------------------------- -PGVERSION="8.4.1" +PGVERSION="`/bin/rpm --queryformat=%{version} -q sepostgresql`" PGMAJORVERSION=`echo "$PGVERSION" | sed 's/^\([0-9]*\.[0-9a-z]*\).*$/\1/'` # source function library @@ -23,21 +23,18 @@ if [ ${NAME:0:1} = "S" -o ${NAME:0:1} = "K" ]; then fi # set defaults for configurable variables -SEPGSQL_BIN="/usr/bin" +SEPGSQL_CTL="/usr/bin/sepg_ctl" SEPGSQL_DATA="/var/lib/sepgsql/data" SEPGSQL_OPTS="-i -p 5432" SEPGSQL_STARTUP_LOG="/var/lib/sepgsql/pgstartup.log" SEPGSQL_LOG="/var/log/sepostgresql.log" -SEPGSQL_FALLBACK_CONTEXT="user_u:user_r:user_t" # override defaults from /etc/sysconfig/sepostgresql test -f /etc/sysconfig/${NAME} && . /etc/sysconfig/${NAME} -export SEPGSQL_FALLBACK_CONTEXT - # Check that networking is up. test "${NETWORKING}" = "no" && exit 0 -test -f "${SEPGSQL_BIN}/sepostgres" || exit 1 +test -f "/usr/bin/sepostgres" || exit 1 script_result=0 @@ -77,11 +74,10 @@ do_start() { chmod 600 ${SEPGSQL_LOG} test -x /sbin/restorecon && /sbin/restorecon ${SEPGSQL_LOG} - cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -c "./sepg_ctl -w -t 10 -l ${SEPGSQL_LOG} -D ${SEPGSQL_DATA} -o '${SEPGSQL_OPTS}' start" \ + /sbin/runuser sepgsql -c "${SEPGSQL_CTL} -w -t 10 -l ${SEPGSQL_LOG} -D ${SEPGSQL_DATA} -o '${SEPGSQL_OPTS}' start" \ >> ${SEPGSQL_STARTUP_LOG} 2>&1 < /dev/null sleep 1 - PID=`/sbin/runuser sepgsql -c "./sepg_ctl -D ${SEPGSQL_DATA} status 2>/dev/null \ + PID=`/sbin/runuser sepgsql -c "${SEPGSQL_CTL} -D ${SEPGSQL_DATA} status 2>/dev/null \ | sed 's/^.*PID: //g' | sed 's/[^0-9].*$//g'"` if [ ${PIPESTATUS[0]} -eq 0 ]; then echo "$PID" > "/var/run/${NAME}.pid" @@ -96,8 +92,7 @@ do_start() { do_stop() { echo -n $"Stopping ${NAME} service: " - cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -c "./sepg_ctl -D ${SEPGSQL_DATA} stop" \ + /sbin/runuser sepgsql -c "${SEPGSQL_CTL} -D ${SEPGSQL_DATA} stop" \ >> ${SEPGSQL_STARTUP_LOG} 2>&1 < /dev/null ret=$? if [ $ret -eq 0 ]; then @@ -112,8 +107,7 @@ do_stop() { } do_status() { - cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -- -c "./sepg_ctl -D ${SEPGSQL_DATA} status" 2>/dev/null \ + /sbin/runuser sepgsql -- -c "${SEPGSQL_CTL} -D ${SEPGSQL_DATA} status" 2>/dev/null \ | head -1 | sed "s/^sepg_ctl:/${NAME}:/g" if [ ${PIPESTATUS[0]} -ne 0 ]; then @@ -125,18 +119,18 @@ do_status() { do_condrestart() { cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -- -c "./sepg_ctl -D ${SEPGSQL_DATA} status" &>/dev/null && do_stop && do_start + /sbin/runuser sepgsql -- -c "${SEPGSQL_CTL} -D ${SEPGSQL_DATA} status" &>/dev/null && do_stop && do_start } do_condstop() { cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -- -c "./sepg_ctl -D ${SEPGSQL_DATA} status" &>/dev/null && do_stop + /sbin/runuser sepgsql -- -c "${SEPGSQL_CTL} -D ${SEPGSQL_DATA} status" &>/dev/null && do_stop } do_reload() { echo -n $"Reloading ${NAME} service: " cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -- -c "./sepg_ctl -D ${SEPGSQL_DATA} reload" &>/dev/null < /dev/null + /sbin/runuser sepgsql -- -c "${SEPGSQL_CTL} -D ${SEPGSQL_DATA} reload" &>/dev/null < /dev/null if [ $? -eq 0 ]; then echo_success else @@ -163,8 +157,7 @@ do_initdb() { # cleanup SELinux labeling for "${SEPGSQL_DATA}" test -x /sbin/restorecon && /sbin/restorecon -R "${SEPGSQL_DATA}" # Initialize the database - cd ${SEPGSQL_BIN} - /sbin/runuser -- sepgsql -c "./initdb.sepgsql --enable-selinux --pgdata='${SEPGSQL_DATA}' --auth='ident'" \ + /sbin/runuser -- sepgsql -c "${SEPGSQL_CTL} initdb -o '--enable-selinux --pgdata=${SEPGSQL_DATA} --auth=ident' -D ${SEPGSQL_DATA}" \ >> "${SEPGSQL_STARTUP_LOG}" 2>&1 < /dev/null if [ -f "${SEPGSQL_DATA}/PG_VERSION" ]; then echo_success diff --git a/sepostgresql.spec b/sepostgresql.spec index bdc0765..89789d8 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -8,22 +8,24 @@ %define selinux_policy_stores targeted mls %{!?ssl:%define ssl 1} +%{!?beta:%define beta .alpha5} Summary: Security Enhanced PostgreSQL Name: sepostgresql -Version: 8.4.3 -Release: 2582%{?dist} -License: BSD +Version: 9.0.0 +Release: 20100404%{?beta}%{?dist} +License: PostgreSQL Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ Buildroot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) -Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.bz2 +#XXX - to be revert later +#Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.bz2 +Source0: postgresql-9.0alpha5.tar.bz2 Source1: sepostgresql.init Source2: sepostgresql.8 Source3: sepostgresql.logrotate Patch0: sepostgresql-fedora-prefix.patch -Patch1: pgsql-01-8.4-blobs.patch -Patch2: pgsql-02-8.4-sepgsql.patch +Patch1: sepostgresql-9.0-fullset.patch BuildRequires: perl glibc-devel bison flex readline-devel zlib-devel >= 1.0.4 BuildRequires: checkpolicy libselinux-devel >= 2.0.80 BuildRequires: selinux-policy >= 3.6.8 @@ -34,7 +36,6 @@ Requires(pre): shadow-utils Requires(post): policycoreutils /sbin/chkconfig Requires(preun): /sbin/chkconfig /sbin/service Requires(postun): policycoreutils -Requires: postgresql-server = %{version} Requires: policycoreutils >= 2.0.16 libselinux >= 2.0.80 Requires: selinux-policy >= 3.6.8 Requires: tzdata logrotate @@ -48,10 +49,11 @@ the operating system. SE-PostgreSQL works as a userspace reference monitor to check any SQL query. %prep -%setup -q -n postgresql-%{version} +#XXX - to be revert later +#%setup -q -n postgresql-%{version} +%setup -q -n postgresql-9.0alpha5 %patch0 -p1 %patch1 -p1 -%patch2 -p1 %build CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS @@ -65,36 +67,38 @@ CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS %endif --enable-debug \ --enable-cassert \ - --libdir=%{_libdir}/pgsql \ - --datadir=%{_datadir}/sepgsql \ --with-system-tzdata=/usr/share/zoneinfo # parallel build, if possible +rm -f src/Makefile.custom make %{?_smp_mflags} -touch src/backend/security/sepgsql/policy/sepostgresql-devel.fc -make -C src/backend/security/sepgsql/policy %install rm -rf %{buildroot} make DESTDIR=%{buildroot} install -install -d %{buildroot}%{_datadir}/selinux/packages -install -p -m 644 src/backend/security/sepgsql/policy/sepostgresql-devel.pp \ - %{buildroot}%{_datadir}/selinux/packages - # avoid to conflict with native postgresql package mv %{buildroot}%{_bindir} %{buildroot}%{_bindir}.orig -install -d %{buildroot}%{_bindir} +install -d %{buildroot}%{_bindir}/ mv %{buildroot}%{_bindir}.orig/initdb %{buildroot}%{_bindir}/initdb.sepgsql mv %{buildroot}%{_bindir}.orig/pg_ctl %{buildroot}%{_bindir}/sepg_ctl mv %{buildroot}%{_bindir}.orig/postgres %{buildroot}%{_bindir}/sepostgres mv %{buildroot}%{_bindir}.orig/pg_dump %{buildroot}%{_bindir}/sepg_dump mv %{buildroot}%{_bindir}.orig/pg_dumpall %{buildroot}%{_bindir}/sepg_dumpall +mv %{buildroot}%{_bindir}.orig/pg_restore %{buildroot}%{_bindir}/sepg_restore + +mv %{buildroot}%{_libdir} %{buildroot}%{_libdir}.orig +install -d %{buildroot}%{_libdir}/sepgsql +mv %{buildroot}%{_libdir}.orig/sepgsql/dict_snowball.so \ + %{buildroot}%{_libdir}.orig/sepgsql/plpgsql.so \ + %{buildroot}%{_libdir}.orig/sepgsql/*_and_*.so \ + %{buildroot}%{_libdir}.orig/sepgsql/euc2004_sjis2004.so \ + %{buildroot}%{_libdir}/sepgsql # remove unnecessary files rm -rf %{buildroot}%{_bindir}.orig -rm -rf %{buildroot}%{_libdir} +rm -rf %{buildroot}%{_libdir}.orig rm -rf %{buildroot}%{_includedir} rm -rf %{buildroot}%{_datadir}/doc rm -rf %{buildroot}%{_datadir}/sepgsql/timezone @@ -123,24 +127,13 @@ rm -rf %{buildroot} %pre getent group sepgsql >/dev/null || groupadd -r sepgsql getent passwd sepgsql >/dev/null || \ - useradd -r -g sepgsql -d %{_localstatedir}/lib/sepgsql -s /bin/bash \ - -c "SE-PostgreSQL server" sepgsql + useradd -r -g sepgsql -s /bin/bash -c "SE-PostgreSQL" sepgsql exit 0 %post /sbin/chkconfig --add %{name} /sbin/ldconfig -for store in %{selinux_policy_stores} -do - # clean up legacy policy module (now it is unnecessary) - %{_sbindir}/semodule -s ${store} -r sepostgresql >& /dev/null || : - if %{_sbindir}/semodule -s ${store} -l 2>/dev/null | grep -Eq "^sepostgresql-devel"; then - %{_sbindir}/semodule -s ${store} \ - -i %{_datadir}/selinux/packages/sepostgresql-devel.pp >& /dev/null || : - fi -done - # Fix up non-standard file contexts /sbin/fixfiles -R %{name} restore || : /sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : @@ -157,12 +150,9 @@ if [ $1 -ge 1 ]; then # rpm -U case /sbin/service %{name} condrestart >/dev/null 2>&1 || : fi if [ $1 -eq 0 ]; then # rpm -e case - for store in %{selinux_policy_stores} - do - %{_sbindir}/semodule -s ${store} -r sepostgresql-devel >& /dev/null || : - done /sbin/fixfiles -R %{name} restore || : - test -d %{_localstatedir}/lib/sepgsql && /sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : + test -d %{_localstatedir}/lib/sepgsql && \ + /sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : fi %files @@ -175,6 +165,8 @@ fi %{_bindir}/sepostgres %{_bindir}/sepg_dump %{_bindir}/sepg_dumpall +%{_bindir}/sepg_restore +%{_libdir}/sepgsql/*.so %{_mandir}/man8/sepostgresql.* %dir %{_datadir}/sepgsql %{_datadir}/sepgsql/postgres.bki @@ -188,12 +180,14 @@ fi %{_datadir}/sepgsql/conversion_create.sql %{_datadir}/sepgsql/information_schema.sql %{_datadir}/sepgsql/sql_features.txt -%attr(644,root,root) %{_datadir}/selinux/packages/sepostgresql-devel.pp %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/data %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog +* Sun Apr 4 2010 KaiGai Kohei - 9.0.0-20100404 +- upgrade base version 8.4.3->9.0alpha5 + * Thu Mar 18 2010 KaiGai Kohei - 8.4.3-2582 - upgrade base version 8.4.2->8.4.3 diff --git a/sources b/sources index 6b61e74..130572c 100644 --- a/sources +++ b/sources @@ -1,2 +1 @@ -d738227e2f1f742d2f2d4ab56496c5c6 postgresql-8.4.2.tar.bz2 -7f70e7b140fb190f268837255582b07e postgresql-8.4.3.tar.bz2 +df7a869e7a1fdbe5c5ffc3928eb3d234 postgresql-9.0alpha5.tar.bz2 From 25dd9900e67a9d5ea5312602f9c46b4411bb7117 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Tue, 11 May 2010 14:37:16 +0000 Subject: [PATCH 09/24] upgrade base postgresql to 9.0beta1 --- .cvsignore | 2 +- sepostgresql-9.0-fullset.patch | 1752 +++++++++++++++++++++----------- sepostgresql.spec | 12 +- sources | 2 +- 4 files changed, 1167 insertions(+), 601 deletions(-) diff --git a/.cvsignore b/.cvsignore index 54ba6ee..15f7b39 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1 @@ -postgresql-9.0alpha5.tar.bz2 +postgresql-9.0beta1.tar.gz diff --git a/sepostgresql-9.0-fullset.patch b/sepostgresql-9.0-fullset.patch index 37109a4..ae16554 100644 --- a/sepostgresql-9.0-fullset.patch +++ b/sepostgresql-9.0-fullset.patch @@ -1,5 +1,5 @@ diff --git a/configure b/configure -index 7e34c4f..7bfa5ff 100755 +index b91de59..b44a6be 100755 --- a/configure +++ b/configure @@ -707,6 +707,7 @@ LDFLAGS_SL @@ -229,7 +229,7 @@ index 7e34c4f..7bfa5ff 100755 # diff --git a/configure.in b/configure.in -index bbeea97..e499e10 100644 +index b2fa682..5797ff5 100644 --- a/configure.in +++ b/configure.in @@ -755,6 +755,19 @@ PGAC_ARG_BOOL(with, zlib, yes, @@ -252,6 +252,293 @@ index bbeea97..e499e10 100644 # Elf # +diff --git a/contrib/adminpack/adminpack.c b/contrib/adminpack/adminpack.c +index 13e04b2..c613826 100644 +--- a/contrib/adminpack/adminpack.c ++++ b/contrib/adminpack/adminpack.c +@@ -22,6 +22,7 @@ + #include "funcapi.h" + #include "miscadmin.h" + #include "postmaster/syslogger.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "utils/builtins.h" + #include "utils/datetime.h" +@@ -142,10 +143,16 @@ pg_file_write(PG_FUNCTION_ARGS) + (ERRCODE_DUPLICATE_FILE, + errmsg("file \"%s\" exists", filename))); + ++ sepgsql_file_write(filename, true); ++ + f = fopen(filename, "wb"); + } + else ++ { ++ sepgsql_file_append(filename); ++ + f = fopen(filename, "ab"); ++ } + + if (!f) + ereport(ERROR, +@@ -217,6 +224,7 @@ pg_file_rename(PG_FUNCTION_ARGS) + + if (fn3) + { ++ sepgsql_file_rename(fn2, fn3); + if (rename(fn2, fn3) != 0) + { + ereport(ERROR, +@@ -224,6 +232,7 @@ pg_file_rename(PG_FUNCTION_ARGS) + errmsg("could not rename \"%s\" to \"%s\": %m", + fn2, fn3))); + } ++ sepgsql_file_rename(fn1, fn2); + if (rename(fn1, fn2) != 0) + { + ereport(WARNING, +@@ -247,11 +256,15 @@ pg_file_rename(PG_FUNCTION_ARGS) + } + } + } +- else if (rename(fn1, fn2) != 0) ++ else + { +- ereport(ERROR, +- (errcode_for_file_access(), +- errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2))); ++ sepgsql_file_rename(fn1, fn2); ++ if (rename(fn1, fn2) != 0) ++ { ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2))); ++ } + } + + PG_RETURN_BOOL(true); +@@ -277,6 +290,8 @@ pg_file_unlink(PG_FUNCTION_ARGS) + errmsg("file \"%s\" is not accessible: %m", filename))); + } + ++ sepgsql_file_unlink(filename); ++ + if (unlink(filename) < 0) + { + ereport(WARNING, +@@ -312,11 +327,14 @@ pg_logdir_ls(PG_FUNCTION_ARGS) + TupleDesc tupdesc; + + funcctx = SRF_FIRSTCALL_INIT(); ++ ++ sepgsql_dir_list(Log_directory); ++ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + fctx = palloc(sizeof(directory_fctx)); + +- tupdesc = CreateTemplateTupleDesc(2, false); ++ tupdesc = CreateTemplateTupleDesc(2, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime", + TIMESTAMPOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename", +diff --git a/contrib/sepgtest/sepgtest b/contrib/sepgtest/sepgtest +new file mode 100755 +index 0000000..30e7cb9 +--- /dev/null ++++ b/contrib/sepgtest/sepgtest +@@ -0,0 +1,45 @@ ++#!/bin/sh ++ ++SEPGTEST=`basename $0` ++PSQL=`which psql` ++DIFF=`which diff` ++RUNCON=`which runcon` ++ ++DBUSER="" ++DBHOST="" ++DBPORT="" ++DBNAME="testdb" ++ ++usage() ++{ ++ echo "${SEPGTEST}: [-u ] [-h ] [-p ] [-d ]" ++ exit 1 ++} ++ ++# parse options ++CMD_OPTS=`getopt -n "${SEPGTEST}" "u:h:p:d:" $*` || usage ++eval set -- $CMD_OPTS ++while [ -n "$1" ]; ++do ++ case $1 in ++ -u) ++ DBUSER="-U $2"; shift 2;; ++ -h) ++ DBHOST="-h $2"; shift 2;; ++ -p) ++ DBPORT="-p $2"; shift 2;; ++ -d) ++ DBNAME="$2"; shift 2;; ++ --) ++ shift; break;; ++ *) ++ usage; shift 2;; ++ esac ++done ++ ++PSQL_OPTS="${PSQL} ${DBUSER} ${DBHOST} ${DBPORT} ${DBNAME}" ++ ++# check environment ++ ++ ++ +diff --git a/contrib/sepgtest/sepgtest.in b/contrib/sepgtest/sepgtest.in +new file mode 100755 +index 0000000..30e7cb9 +--- /dev/null ++++ b/contrib/sepgtest/sepgtest.in +@@ -0,0 +1,45 @@ ++#!/bin/sh ++ ++SEPGTEST=`basename $0` ++PSQL=`which psql` ++DIFF=`which diff` ++RUNCON=`which runcon` ++ ++DBUSER="" ++DBHOST="" ++DBPORT="" ++DBNAME="testdb" ++ ++usage() ++{ ++ echo "${SEPGTEST}: [-u ] [-h ] [-p ] [-d ]" ++ exit 1 ++} ++ ++# parse options ++CMD_OPTS=`getopt -n "${SEPGTEST}" "u:h:p:d:" $*` || usage ++eval set -- $CMD_OPTS ++while [ -n "$1" ]; ++do ++ case $1 in ++ -u) ++ DBUSER="-U $2"; shift 2;; ++ -h) ++ DBHOST="-h $2"; shift 2;; ++ -p) ++ DBPORT="-p $2"; shift 2;; ++ -d) ++ DBNAME="$2"; shift 2;; ++ --) ++ shift; break;; ++ *) ++ usage; shift 2;; ++ esac ++done ++ ++PSQL_OPTS="${PSQL} ${DBUSER} ${DBHOST} ${DBPORT} ${DBNAME}" ++ ++# check environment ++ ++ ++ +diff --git a/git-update-branches.sh b/git-update-branches.sh +new file mode 100755 +index 0000000..2f25fde +--- /dev/null ++++ b/git-update-branches.sh +@@ -0,0 +1,89 @@ ++#!/bin/sh ++ ++GIT_BIN=`which git` ++GIT_DIR=`(cd \`dirname $0\`; pwd)` ++UPSTREAM_URL="git://git.postgresql.org/git/postgresql.git" ++UPSTREAM_BRANCH="master" ++LOCAL_BRANCHES="master v9.0/sepgsql" ++RPMSOURCE=`rpm -E '%{_sourcedir}'` ++#================================================================ ++ ++cd ${GIT_DIR} ++ ++CURRENT_BRANCH=`${GIT_BIN} branch -l | grep '^*' | awk '{print $2}'` ++ ++for tree in ${LOCAL_BRANCHES} ++do ++ ${GIT_BIN} branch -lr | grep -q "origin/${tree}" || exit 1 ++ ${GIT_BIN} branch -l | grep -q "${tree}" || \ ++ ${GIT_BIN} checkout --track "origin/${tree}" || exit 1 ++done ++ ++# ======== create patches ======== ++if [ "$1" = "--patch" ]; then ++ LEFT_TREE="" ++ RIGHT_TREE="" ++ NUM=0 ++ ++ BASE_VERSION=`grep AC_INIT ${GIT_DIR}/configure.in \ ++ | head -1 \ ++ | sed -e 's/,/ /g' -e 's/\[//g' -e 's/\]//g' \ ++ | awk '{print $2}'` ++ BASE_MAJOR=`echo $BASE_VERSION | sed 's/\.[0-9]\+$//g'` ++ ++ for tree in ${LOCAL_BRANCHES} ++ do ++ LEFT_TREE=$RIGHT_TREE ++ RIGHT_TREE=$tree ++ ++ if [ -n "$LEFT_TREE" -a -n "$RIGHT_TREE" ]; then ++ tag=`basename ${RIGHT_TREE}` ++ ${GIT_BIN} diff $LEFT_TREE $RIGHT_TREE \ ++ > $RPMSOURCE/pgsql-${NUM}-${BASE_MAJOR}-${tag}.patch ++ echo "${NUM}) $RPMSOURCE/pgsql-${NUM}-${BASE_MAJOR}-${tag}.patch" ++ fi ++ NUM=`printf "%02d" \`expr $NUM + 1\`` ++ done ++ ++ exit 0 ++fi ++ ++# ======== sync remote git ======== ++ ++if echo "$1" | grep -q '^--sync-upstream=\?'; then ++ UPSTREAM_TAG=`echo "$1" | sed 's/^--sync-upstream=\?//g'` ++ if [ -n "${UPSTREAM_TAG}" ]; then ++ UPSTREAM_TAG=HEAD ++ fi ++ ++ LOCAL_MASTER=`echo "${LOCAL_BRANCHES}" | awk '{print $1}'` ++ ++ ${GIT_BIN} checkout ${LOCAL_MASTER} || exit 1 ++ ${GIT_BIN} pull ${UPSTREAM_URL} ${UPSTREAM_BRANCH} ${UPSTREAM_TAG} ++ if [ $? -ne 0 ]; then ++ ${GIT_BIN} reset HEAD ++ ${GIT_BIN} checkout . ++ ${GIT_BIN} clean -fd ++ ${GIT_BIN} pull --no-commit ${UPSTREAM_URL} ${UPSTREAM_BRANCH} ${UPSTREAM_TAG} ++ exit 2 ++ fi ++fi ++ ++LEFT_TREE="" ++for RIGHT_TREE in ${LOCAL_BRANCHES} ++do ++ if [ -n "${LEFT_TREE}" -a -n "${RIGHT_TREE}" ]; then ++ ${GIT_BIN} checkout ${RIGHT_TREE} || exit 1 ++ ${GIT_BIN} pull ${GIT_DIR} ${LEFT_TREE} || exit 1 ++ if [ $? -ne 0 ]; then ++ ${GIT_BIN} reset HEAD ++ ${GIT_BIN} checkout . ++ ${GIT_BIN} clean -fd ++ ${GIT_BIN} pull --no-commit ${GIT_DIR} ${LEFT_TREE} ++ exit 2 ++ fi ++ fi ++ LEFT_TREE=${RIGHT_TREE} ++done ++ ++${GIT_BIN} checkout ${CURRENT_BRANCH} diff --git a/src/Makefile.global.in b/src/Makefile.global.in index 1c38ac2..0e3bed5 100644 --- a/src/Makefile.global.in @@ -464,7 +751,7 @@ index f01ed1e..4e8dfcb 100644 TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL, INT2OID, -1, 0); diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c -index bb57cb9..d17274f 100644 +index 759fc35..2fc282d 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -52,6 +52,7 @@ @@ -562,7 +849,7 @@ index 2af81df..18341f4 100644 Assert(new_len == olddata->t_hoff); new_data_len = heap_compute_data_size(tupleDesc, diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c -index e2566a4..c21054b 100644 +index d432c9d..28e7bde 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -605,7 +605,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS) @@ -647,10 +934,10 @@ index b88cff2..d6636f7 100644 CurrentTransactionState = s; diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c -index abdf4d8..1b5b84c 100644 +index 0b05e25..4f783e5 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c -@@ -8427,7 +8427,7 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS) +@@ -8765,7 +8765,7 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS) * Construct a tuple descriptor for the result row. This must match this * function's pg_proc entry! */ @@ -695,7 +982,7 @@ index 387d43e..aecb449 100644 } do_end(); diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c -index 46e8bae..5f8c824 100644 +index 455eac1..0590156 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -33,6 +33,7 @@ @@ -716,7 +1003,7 @@ index 46e8bae..5f8c824 100644 default: statmsg = "??? process"; break; -@@ -429,6 +433,10 @@ AuxiliaryProcessMain(int argc, char *argv[]) +@@ -428,6 +432,10 @@ AuxiliaryProcessMain(int argc, char *argv[]) WalReceiverMain(); proc_exit(1); /* should never return */ @@ -727,7 +1014,7 @@ index 46e8bae..5f8c824 100644 default: elog(PANIC, "unrecognized process type: %d", auxType); proc_exit(1); -@@ -493,6 +501,11 @@ BootstrapModeMain(void) +@@ -482,6 +490,11 @@ BootstrapModeMain(void) boot_yyparse(); /* @@ -739,7 +1026,7 @@ index 46e8bae..5f8c824 100644 * We should now know about all mapped relations, so it's okay to write * out the initial relation mapping files. */ -@@ -794,6 +807,7 @@ InsertOneTuple(Oid objectid) +@@ -779,6 +792,7 @@ InsertOneTuple(Oid objectid) tupDesc = CreateTupleDesc(numattr, RelationGetForm(boot_reldesc)->relhasoids, @@ -770,7 +1057,7 @@ index dafae3f..c2aea7a 100644 pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ pg_default_acl.h \ diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c -index d16b03b..a9d5184 100644 +index 2561d83..002a66c 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -38,6 +38,7 @@ @@ -789,7 +1076,7 @@ index d16b03b..a9d5184 100644 #include "utils/acl.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" -@@ -1460,6 +1462,10 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, +@@ -1499,6 +1501,10 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) continue; @@ -800,7 +1087,7 @@ index d16b03b..a9d5184 100644 /* Views don't have any system columns at all */ if (classForm->relkind == RELKIND_VIEW && curr_att < 0) continue; -@@ -1560,6 +1566,8 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, +@@ -1608,6 +1614,8 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, relOid, grantorId, ACL_KIND_COLUMN, relname, attnum, NameStr(pg_attribute_tuple->attname)); @@ -809,7 +1096,7 @@ index d16b03b..a9d5184 100644 /* * Generate new ACL. -@@ -1813,6 +1821,8 @@ ExecGrant_Relation(InternalGrant *istmt) +@@ -1869,6 +1877,8 @@ ExecGrant_Relation(InternalGrant *istmt) ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, NameStr(pg_class_tuple->relname), 0, NULL); @@ -818,7 +1105,7 @@ index d16b03b..a9d5184 100644 /* * Generate new ACL. -@@ -1999,6 +2009,8 @@ ExecGrant_Database(InternalGrant *istmt) +@@ -2063,6 +2073,8 @@ ExecGrant_Database(InternalGrant *istmt) datId, grantorId, ACL_KIND_DATABASE, NameStr(pg_database_tuple->datname), 0, NULL); @@ -827,7 +1114,7 @@ index d16b03b..a9d5184 100644 /* * Generate new ACL. -@@ -2116,6 +2128,8 @@ ExecGrant_Fdw(InternalGrant *istmt) +@@ -2188,6 +2200,8 @@ ExecGrant_Fdw(InternalGrant *istmt) fdwid, grantorId, ACL_KIND_FDW, NameStr(pg_fdw_tuple->fdwname), 0, NULL); @@ -836,7 +1123,7 @@ index d16b03b..a9d5184 100644 /* * Generate new ACL. -@@ -2233,6 +2247,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) +@@ -2313,6 +2327,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) srvid, grantorId, ACL_KIND_FOREIGN_SERVER, NameStr(pg_server_tuple->srvname), 0, NULL); @@ -845,7 +1132,7 @@ index d16b03b..a9d5184 100644 /* * Generate new ACL. -@@ -2719,6 +2735,8 @@ ExecGrant_Namespace(InternalGrant *istmt) +@@ -2831,6 +2847,8 @@ ExecGrant_Namespace(InternalGrant *istmt) nspid, grantorId, ACL_KIND_NAMESPACE, NameStr(pg_namespace_tuple->nspname), 0, NULL); @@ -854,7 +1141,7 @@ index d16b03b..a9d5184 100644 /* * Generate new ACL. -@@ -2835,6 +2853,8 @@ ExecGrant_Tablespace(InternalGrant *istmt) +@@ -2955,6 +2973,8 @@ ExecGrant_Tablespace(InternalGrant *istmt) tblId, grantorId, ACL_KIND_TABLESPACE, NameStr(pg_tablespace_tuple->spcname), 0, NULL); @@ -864,7 +1151,7 @@ index d16b03b..a9d5184 100644 /* * Generate new ACL. diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c -index 7efdf67..9376f6c 100644 +index 3edfc23..b451e59 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -32,6 +32,7 @@ @@ -892,9 +1179,9 @@ index 7efdf67..9376f6c 100644 relationId == SharedDescriptionObjIndexId || relationId == SharedDependDependerIndexId || relationId == SharedDependReferenceIndexId || -@@ -331,6 +335,8 @@ IsSharedRelation(Oid relationId) - relationId == PgAuthidToastIndex || - relationId == PgDatabaseToastTable || +@@ -329,6 +333,8 @@ IsSharedRelation(Oid relationId) + /* These are their toast tables and toast indexes (see toasting.h) */ + if (relationId == PgDatabaseToastTable || relationId == PgDatabaseToastIndex || + relationId == PgSecLabelToastTable || + relationId == PgSecLabelToastIndex || @@ -902,10 +1189,10 @@ index 7efdf67..9376f6c 100644 relationId == PgShdescriptionToastIndex || relationId == PgDbRoleSettingToastTable || diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl -index 9eb805d..30a0779 100644 +index 31aabda..c9fc09e 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl -@@ -216,7 +216,8 @@ foreach my $catname ( @{ $catalogs->{names} } ) +@@ -218,7 +218,8 @@ foreach my $catname ( @{ $catalogs->{names} } ) {cmin => 'cid'}, {xmax => 'xid'}, {cmax => 'cid'}, @@ -916,7 +1203,7 @@ index 9eb805d..30a0779 100644 foreach my $attr (@SYS_ATTRS) { diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c -index ec89e49..35bc618 100644 +index d848ef0..deaf2fb 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -43,6 +43,7 @@ @@ -1732,7 +2019,7 @@ index 2362268..f6df55e 100644 } diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c -index 3772c32..d99c351 100644 +index d76e415..f4c74a3 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -84,7 +84,8 @@ ProcedureCreate(const char *procedureName, @@ -2626,7 +2913,7 @@ index 17e1e77..c1c8bfd 100644 + } +} diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c -index 30a00ab..da39665 100644 +index ccb4599..394740a 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -30,12 +30,14 @@ @@ -2694,7 +2981,7 @@ index 30a00ab..da39665 100644 ReleaseSysCache(tuple); -@@ -994,6 +1007,9 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, +@@ -977,6 +990,9 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, /* Preserve OID, if any */ if (NewHeap->rd_rel->relhasoids) HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); @@ -2704,7 +2991,7 @@ index 30a00ab..da39665 100644 /* The heap rewrite module does the rest */ rewrite_heap_tuple(rwstate, tuple, copiedTuple); -@@ -1481,7 +1497,8 @@ get_tables_to_cluster(MemoryContext cluster_context) +@@ -1464,7 +1480,8 @@ get_tables_to_cluster(MemoryContext cluster_context) { index = (Form_pg_index) GETSTRUCT(indexTuple); @@ -3002,7 +3289,7 @@ index 57ddab0..0c10a64 100644 /* * Modify the owner --- okay to scribble on tup because it's a copy diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c -index 84a83f1..7cf7a89 100644 +index 9d46e47..270eb74 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -22,7 +22,10 @@ @@ -3046,8 +3333,19 @@ index 84a83f1..7cf7a89 100644 Datum *values, bool *nulls); static void CopyFrom(CopyState cstate); static bool CopyReadLine(CopyState cstate); -@@ -988,6 +998,10 @@ DoCopy(const CopyStmt *stmt, const char *queryString) +@@ -986,8 +996,21 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + errhint("Anyone can COPY to stdout or from stdin. " + "psql's \\copy command also works for anyone."))); ++ /* SELinux checks on file COPY */ ++ if (stmt->filename) ++ { ++ if (is_from) ++ sepgsql_file_read(stmt->filename); ++ else ++ sepgsql_file_write(stmt->filename, true); ++ } ++ if (stmt->relation) { + Bitmapset *columnsSet = NULL; @@ -3057,7 +3355,7 @@ index 84a83f1..7cf7a89 100644 Assert(!stmt->query); cstate->queryDesc = NULL; -@@ -998,16 +1012,20 @@ DoCopy(const CopyStmt *stmt, const char *queryString) +@@ -998,16 +1021,20 @@ DoCopy(const CopyStmt *stmt, const char *queryString) tupDesc = RelationGetDescr(cstate->rel); /* Check relation permissions. */ @@ -3082,7 +3380,7 @@ index 84a83f1..7cf7a89 100644 foreach(cur, attnums) { int attnum = lfirst_int(cur); -@@ -1021,6 +1039,13 @@ DoCopy(const CopyStmt *stmt, const char *queryString) +@@ -1021,6 +1048,13 @@ DoCopy(const CopyStmt *stmt, const char *queryString) } } @@ -3096,7 +3394,7 @@ index 84a83f1..7cf7a89 100644 /* check read-only transaction */ if (XactReadOnly && is_from && !cstate->rel->rd_islocaltemp) PreventCommandIfReadOnly("COPY FROM"); -@@ -1130,11 +1155,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString) +@@ -1130,11 +1164,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString) int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) @@ -3123,7 +3421,7 @@ index 84a83f1..7cf7a89 100644 } } -@@ -1152,10 +1190,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString) +@@ -1152,10 +1199,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString) int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) @@ -3149,7 +3447,7 @@ index 84a83f1..7cf7a89 100644 cstate->force_notnull_flags[attnum - 1] = true; } } -@@ -1347,16 +1399,29 @@ CopyTo(CopyState cstate) +@@ -1347,16 +1408,29 @@ CopyTo(CopyState cstate) int attnum = lfirst_int(cur); Oid out_func_oid; bool isvarlena; @@ -3182,7 +3480,7 @@ index 84a83f1..7cf7a89 100644 } /* -@@ -1411,7 +1476,17 @@ CopyTo(CopyState cstate) +@@ -1411,7 +1485,17 @@ CopyTo(CopyState cstate) CopySendChar(cstate, cstate->delim[0]); hdr_delim = true; @@ -3201,7 +3499,15 @@ index 84a83f1..7cf7a89 100644 CopyAttributeOutCSV(cstate, colname, false, list_length(cstate->attnumlist) == 1); -@@ -1441,7 +1516,7 @@ CopyTo(CopyState cstate) +@@ -1437,11 +1521,15 @@ CopyTo(CopyState cstate) + { + CHECK_FOR_INTERRUPTS(); + ++ /* Row-level access control policy */ ++ if (!sepgsql_rowlv_copyto(cstate->rel, tuple)) ++ continue; ++ + /* Deconstruct the tuple ... faster than repeated heap_getattr */ heap_deform_tuple(tuple, tupDesc, values, nulls); /* Format and send the data */ @@ -3210,7 +3516,7 @@ index 84a83f1..7cf7a89 100644 } heap_endscan(scandesc); -@@ -1467,13 +1542,17 @@ CopyTo(CopyState cstate) +@@ -1467,13 +1555,17 @@ CopyTo(CopyState cstate) * Emit one row during CopyTo(). */ static void @@ -3229,7 +3535,7 @@ index 84a83f1..7cf7a89 100644 MemoryContextReset(cstate->rowcontext); oldcontext = MemoryContextSwitchTo(cstate->rowcontext); -@@ -1506,8 +1585,10 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) +@@ -1506,8 +1598,10 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); @@ -3242,7 +3548,7 @@ index 84a83f1..7cf7a89 100644 if (!cstate->binary) { -@@ -1516,6 +1597,21 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) +@@ -1516,6 +1610,21 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) need_delim = true; } @@ -3264,7 +3570,7 @@ index 84a83f1..7cf7a89 100644 if (isnull) { if (!cstate->binary) -@@ -1527,11 +1623,9 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) +@@ -1527,11 +1636,9 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) { if (!cstate->binary) { @@ -3278,7 +3584,7 @@ index 84a83f1..7cf7a89 100644 list_length(cstate->attnumlist) == 1); else CopyAttributeOutText(cstate, string); -@@ -1540,8 +1634,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) +@@ -1540,8 +1647,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) { bytea *outputbytes; @@ -3288,7 +3594,7 @@ index 84a83f1..7cf7a89 100644 CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); CopySendData(cstate, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); -@@ -1675,8 +1768,10 @@ CopyFrom(CopyState cstate) +@@ -1675,8 +1781,10 @@ CopyFrom(CopyState cstate) num_defaults; FmgrInfo *in_functions; FmgrInfo oid_in_function; @@ -3299,7 +3605,7 @@ index 84a83f1..7cf7a89 100644 int attnum; int i; Oid in_func_oid; -@@ -1919,6 +2014,19 @@ CopyFrom(CopyState cstate) +@@ -1919,6 +2027,19 @@ CopyFrom(CopyState cstate) fmgr_info(in_func_oid, &oid_in_function); } @@ -3319,7 +3625,7 @@ index 84a83f1..7cf7a89 100644 values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); -@@ -1953,6 +2061,7 @@ CopyFrom(CopyState cstate) +@@ -1953,6 +2074,7 @@ CopyFrom(CopyState cstate) { bool skip_tuple; Oid loaded_oid = InvalidOid; @@ -3327,7 +3633,7 @@ index 84a83f1..7cf7a89 100644 CHECK_FOR_INTERRUPTS(); -@@ -2024,14 +2133,20 @@ CopyFrom(CopyState cstate) +@@ -2024,14 +2146,20 @@ CopyFrom(CopyState cstate) /* Loop to read the user attributes on the line. */ foreach(cur, cstate->attnumlist) { @@ -3349,7 +3655,7 @@ index 84a83f1..7cf7a89 100644 string = field_strings[fieldno++]; if (cstate->csv_mode && string == NULL && -@@ -2041,14 +2156,38 @@ CopyFrom(CopyState cstate) +@@ -2041,14 +2169,38 @@ CopyFrom(CopyState cstate) string = cstate->null_print; } @@ -3395,7 +3701,7 @@ index 84a83f1..7cf7a89 100644 cstate->cur_attname = NULL; cstate->cur_attval = NULL; } -@@ -2094,17 +2233,38 @@ CopyFrom(CopyState cstate) +@@ -2094,17 +2246,38 @@ CopyFrom(CopyState cstate) i = 0; foreach(cur, cstate->attnumlist) { @@ -3441,7 +3747,7 @@ index 84a83f1..7cf7a89 100644 cstate->cur_attname = NULL; } } -@@ -2125,6 +2285,8 @@ CopyFrom(CopyState cstate) +@@ -2125,6 +2298,8 @@ CopyFrom(CopyState cstate) if (cstate->oids && file_has_oids) HeapTupleSetOid(tuple, loaded_oid); @@ -3450,7 +3756,7 @@ index 84a83f1..7cf7a89 100644 /* Triggers and stuff need to be invoked in query context. */ MemoryContextSwitchTo(oldcontext); -@@ -2149,6 +2311,9 @@ CopyFrom(CopyState cstate) +@@ -2149,6 +2324,9 @@ CopyFrom(CopyState cstate) } if (!skip_tuple) @@ -3460,7 +3766,7 @@ index 84a83f1..7cf7a89 100644 { List *recheckIndexes = NIL; -@@ -3442,6 +3607,17 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) +@@ -3435,6 +3613,17 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) } if (attnum == InvalidAttrNumber) { @@ -3478,7 +3784,7 @@ index 84a83f1..7cf7a89 100644 if (rel != NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), -@@ -3489,7 +3665,7 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self) +@@ -3482,7 +3671,7 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self) slot_getallattrs(slot); /* And send the data */ @@ -4551,7 +4857,7 @@ index f52e1d8..ecff05d 100644 if (!seqrel->rd_islocaltemp) PreventCommandIfReadOnly("setval()"); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c -index 25b2807..cbe483b 100644 +index 9b5ce65..34ffbe2 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -32,6 +32,7 @@ @@ -4862,7 +5168,7 @@ index 25b2807..cbe483b 100644 } /* Now check any constraints on the possibly-changed tuple */ -@@ -3608,7 +3695,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, +@@ -3601,7 +3688,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, @@ -4871,7 +5177,7 @@ index 25b2807..cbe483b 100644 { Oid myrelid = RelationGetRelid(rel); Relation pgclass, -@@ -3622,12 +3709,16 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3615,12 +3702,16 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, int32 typmod; Form_pg_type tform; Expr *defval; @@ -4888,7 +5194,7 @@ index 25b2807..cbe483b 100644 attrdesc = heap_open(AttributeRelationId, RowExclusiveLock); /* -@@ -3655,6 +3746,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3648,6 +3739,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, errmsg("child table \"%s\" has different type for column \"%s\"", RelationGetRelationName(rel), colDef->colname))); @@ -4902,7 +5208,7 @@ index 25b2807..cbe483b 100644 /* If it's OID, child column must actually be OID */ if (isOid && childatt->attnum != ObjectIdAttributeNumber) ereport(ERROR, -@@ -3662,6 +3760,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3655,6 +3753,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, errmsg("child table \"%s\" has a conflicting \"%s\" column", RelationGetRelationName(rel), colDef->colname))); @@ -4916,7 +5222,7 @@ index 25b2807..cbe483b 100644 /* Bump the existing child att's inhcount */ childatt->attinhcount++; simple_heap_update(attrdesc, &tuple->t_self, tuple); -@@ -3701,6 +3806,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3694,6 +3799,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, /* Determine the new attribute's number */ if (isOid) newattnum = ObjectIdAttributeNumber; @@ -4925,7 +5231,7 @@ index 25b2807..cbe483b 100644 else { newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1; -@@ -3740,7 +3847,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3733,7 +3840,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ReleaseSysCache(typeTuple); @@ -4934,7 +5240,7 @@ index 25b2807..cbe483b 100644 heap_close(attrdesc, RowExclusiveLock); -@@ -3749,6 +3856,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3742,6 +3849,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, */ if (isOid) ((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true; @@ -4943,7 +5249,7 @@ index 25b2807..cbe483b 100644 else ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum; -@@ -3860,7 +3969,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3853,7 +3962,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, * If we are adding an OID column, we have to tell Phase 3 to rewrite the * table to fix that. */ @@ -4952,7 +5258,7 @@ index 25b2807..cbe483b 100644 tab->new_changeoids = true; /* -@@ -3913,6 +4022,31 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) +@@ -3906,6 +4015,31 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) } /* @@ -4984,7 +5290,7 @@ index 25b2807..cbe483b 100644 * ALTER TABLE ALTER COLUMN DROP NOT NULL */ static void -@@ -3924,6 +4058,9 @@ ATExecDropNotNull(Relation rel, const char *colName) +@@ -3917,6 +4051,9 @@ ATExecDropNotNull(Relation rel, const char *colName) List *indexoidlist; ListCell *indexoidscan; @@ -4994,7 +5300,7 @@ index 25b2807..cbe483b 100644 /* * lookup the attribute */ -@@ -4014,6 +4151,9 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, +@@ -4007,6 +4144,9 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, AttrNumber attnum; Relation attr_rel; @@ -5004,7 +5310,7 @@ index 25b2807..cbe483b 100644 /* * lookup the attribute */ -@@ -4064,6 +4204,9 @@ ATExecColumnDefault(Relation rel, const char *colName, +@@ -4057,6 +4197,9 @@ ATExecColumnDefault(Relation rel, const char *colName, { AttrNumber attnum; @@ -5014,7 +5320,7 @@ index 25b2807..cbe483b 100644 /* * get the number of the attribute */ -@@ -4138,6 +4281,9 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue) +@@ -4131,6 +4274,9 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue) HeapTuple tuple; Form_pg_attribute attrtuple; @@ -5024,7 +5330,7 @@ index 25b2807..cbe483b 100644 Assert(IsA(newValue, Integer)); newtarget = intVal(newValue); -@@ -4204,6 +4350,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, +@@ -4197,6 +4343,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, bool repl_null[Natts_pg_attribute]; bool repl_repl[Natts_pg_attribute]; @@ -5034,7 +5340,7 @@ index 25b2807..cbe483b 100644 attrelation = heap_open(AttributeRelationId, RowExclusiveLock); tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName); -@@ -4263,6 +4412,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue) +@@ -4256,6 +4405,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue) HeapTuple tuple; Form_pg_attribute attrtuple; @@ -5044,7 +5350,7 @@ index 25b2807..cbe483b 100644 Assert(IsA(newValue, String)); storagemode = strVal(newValue); -@@ -4353,6 +4505,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, +@@ -4346,6 +4498,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, if (recursing) ATSimplePermissions(rel, false); @@ -5054,7 +5360,7 @@ index 25b2807..cbe483b 100644 /* * get the number of the attribute */ -@@ -4378,8 +4533,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, +@@ -4371,8 +4526,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, attnum = targetatt->attnum; @@ -5067,7 +5373,7 @@ index 25b2807..cbe483b 100644 ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot drop system column \"%s\"", -@@ -4495,7 +4652,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, +@@ -4488,7 +4645,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, * If we dropped the OID column, must adjust pg_class.relhasoids and tell * Phase 3 to physically get rid of the column. */ @@ -5077,7 +5383,7 @@ index 25b2807..cbe483b 100644 { Relation class_rel; Form_pg_class tuple_class; -@@ -4510,7 +4668,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, +@@ -4503,7 +4661,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, RelationGetRelid(rel)); tuple_class = (Form_pg_class) GETSTRUCT(tuple); @@ -5090,7 +5396,7 @@ index 25b2807..cbe483b 100644 simple_heap_update(class_rel, &tuple->t_self, tuple); /* Keep the catalog indexes up to date */ -@@ -4657,6 +4819,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, +@@ -4650,6 +4812,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, if (recursing) ATSimplePermissions(rel, false); @@ -5100,7 +5406,7 @@ index 25b2807..cbe483b 100644 /* * Call AddRelationNewConstraints to do the work, making sure it works on * a copy of the Constraint so transformExpr can't modify the original. It -@@ -4854,6 +5019,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, +@@ -4847,6 +5012,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, checkFkeyPermissions(pkrel, pkattnum, numpks); checkFkeyPermissions(rel, fkattnum, numfks); @@ -5110,7 +5416,7 @@ index 25b2807..cbe483b 100644 /* * Look up the equality operators to use in the constraint. * -@@ -5599,6 +5767,9 @@ ATExecDropConstraint(Relation rel, const char *constrName, +@@ -5592,6 +5760,9 @@ ATExecDropConstraint(Relation rel, const char *constrName, if (recursing) ATSimplePermissions(rel, false); @@ -5120,7 +5426,7 @@ index 25b2807..cbe483b 100644 conrel = heap_open(ConstraintRelationId, RowExclusiveLock); /* -@@ -5931,6 +6102,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, +@@ -5924,6 +6095,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, SysScanDesc scan; HeapTuple depTup; @@ -5130,7 +5436,7 @@ index 25b2807..cbe483b 100644 attrelation = heap_open(AttributeRelationId, RowExclusiveLock); /* Look up the target column */ -@@ -6544,6 +6718,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) +@@ -6537,6 +6711,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } @@ -5139,7 +5445,7 @@ index 25b2807..cbe483b 100644 } memset(repl_null, false, sizeof(repl_null)); -@@ -6709,6 +6885,9 @@ ATExecClusterOn(Relation rel, const char *indexName) +@@ -6702,6 +6878,9 @@ ATExecClusterOn(Relation rel, const char *indexName) { Oid indexOid; @@ -5149,7 +5455,7 @@ index 25b2807..cbe483b 100644 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace); if (!OidIsValid(indexOid)) -@@ -6733,6 +6912,9 @@ ATExecClusterOn(Relation rel, const char *indexName) +@@ -6726,6 +6905,9 @@ ATExecClusterOn(Relation rel, const char *indexName) static void ATExecDropCluster(Relation rel) { @@ -5159,7 +5465,7 @@ index 25b2807..cbe483b 100644 mark_index_clustered(rel, InvalidOid); } -@@ -6783,6 +6965,9 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) +@@ -6776,6 +6958,9 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) bool repl_repl[Natts_pg_class]; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; @@ -5169,7 +5475,7 @@ index 25b2807..cbe483b 100644 if (defList == NIL) return; /* nothing to do */ -@@ -7115,6 +7300,9 @@ static void +@@ -7090,6 +7275,9 @@ static void ATExecEnableDisableTrigger(Relation rel, char *trigname, char fires_when, bool skip_system) { @@ -5179,7 +5485,7 @@ index 25b2807..cbe483b 100644 EnableDisableTrigger(rel, trigname, fires_when, skip_system); } -@@ -7127,6 +7315,9 @@ static void +@@ -7102,6 +7290,9 @@ static void ATExecEnableDisableRule(Relation rel, char *trigname, char fires_when) { @@ -5189,7 +5495,7 @@ index 25b2807..cbe483b 100644 EnableDisableRule(rel, trigname, fires_when); } -@@ -7160,6 +7351,10 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent) +@@ -7135,6 +7326,10 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent) */ ATSimplePermissions(parent_rel, false); @@ -5200,7 +5506,7 @@ index 25b2807..cbe483b 100644 /* Permanent rels cannot inherit from temporary ones */ if (parent_rel->rd_istemp && !child_rel->rd_istemp) ereport(ERROR, -@@ -7512,6 +7707,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent) +@@ -7487,6 +7682,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent) List *connames; bool found = false; @@ -5210,7 +5516,7 @@ index 25b2807..cbe483b 100644 /* * AccessShareLock on the parent is probably enough, seeing that DROP * TABLE doesn't lock parent tables at all. We need some lock since we'll -@@ -7809,6 +8007,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, +@@ -7784,6 +7982,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, /* get schema OID and check its permissions */ nspOid = LookupCreationNamespace(newschema); @@ -5220,7 +5526,7 @@ index 25b2807..cbe483b 100644 if (oldNspOid == nspOid) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE), -@@ -8006,6 +8207,282 @@ AlterSeqNamespaces(Relation classRel, Relation rel, +@@ -7981,6 +8182,282 @@ AlterSeqNamespaces(Relation classRel, Relation rel, relation_close(depRel, AccessShareLock); } @@ -6377,7 +6683,7 @@ index 5e555ad..963096d 100644 /* * Use the given slot, or make a new slot if we weren't given one. diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c -index d5e7e3a..96d4ba8 100644 +index d299310..febb02e 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -38,6 +38,7 @@ @@ -6499,7 +6805,7 @@ index d5e7e3a..96d4ba8 100644 FreeTupleDesc(tupdesc); -@@ -2305,6 +2359,11 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self) +@@ -2298,6 +2352,11 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self) */ if (myState->rel->rd_rel->relhasoids) HeapTupleSetOid(tuple, InvalidOid); @@ -6562,10 +6868,59 @@ index 005e15e..6e93932 100644 fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), econtext->ecxt_per_query_memory); diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c -index 53fe195..cb63671 100644 +index 53fe195..68f790f 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c -@@ -258,6 +258,7 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc +@@ -20,6 +20,7 @@ + + #include "executor/executor.h" + #include "miscadmin.h" ++#include "parser/parsetree.h" + #include "utils/memutils.h" + + +@@ -114,6 +115,9 @@ ExecScan(ScanState *node, + ProjectionInfo *projInfo; + ExprDoneCond isDone; + TupleTableSlot *resultSlot; ++ Scan *scan = (Scan *)node->ps.plan; ++ EState *estate = node->ps.state; ++ RangeTblEntry *rte = rt_fetch(scan->scanrelid, estate->es_range_table); + + /* + * Fetch data from node +@@ -125,7 +129,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 && !rte->rowlvPerms) + return ExecScanFetch(node, accessMtd, recheckMtd); + + /* +@@ -189,9 +193,20 @@ ExecScan(ScanState *node, + * when the qual is nil ... saves only a few cycles, but they add up + * ... + */ +- if (!qual || ExecQual(qual, econtext, false)) ++ if (sepgsql_rowlv_quals(node->ss_currentRelation, ++ rte->rowlvPerms, slot, false) && ++ (!qual || ExecQual(qual, econtext, false))) + { + /* ++ * NOTE: When FK checks, the Row-level features needs to raise ++ * an error after evaluation of all the given quals, to avoid ++ * incorrect error reports about unrelated tuples. ++ * We assume FK does not use malicious functions in the quals. ++ */ ++ sepgsql_rowlv_quals(node->ss_currentRelation, ++ rte->rowlvPerms, slot, true); ++ ++ /* + * Found a satisfactory scan tuple. + */ + if (projInfo) +@@ -258,6 +273,7 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc int numattrs = tupdesc->natts; int attrno; bool hasoid; @@ -6573,7 +6928,7 @@ index 53fe195..cb63671 100644 ListCell *tlist_item = list_head(tlist); /* Check the tlist attributes */ -@@ -307,6 +308,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc +@@ -307,6 +323,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc if (ExecContextForcesOids(ps, &hasoid) && hasoid != tupdesc->tdhasoid) return false; @@ -7334,28 +7689,35 @@ index bc6e2a6..792b9ff 100644 READ_DONE(); } -diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c -index a0e31a2..086b313 100644 ---- a/src/backend/optimizer/plan/createplan.c -+++ b/src/backend/optimizer/plan/createplan.c -@@ -32,6 +32,7 @@ - #include "optimizer/var.h" - #include "parser/parse_clause.h" +diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c +index 3950ab4..18e5d80 100644 +--- a/src/backend/optimizer/plan/planner.c ++++ b/src/backend/optimizer/plan/planner.c +@@ -39,6 +39,7 @@ + #include "parser/parse_expr.h" + #include "parser/parse_oper.h" #include "parser/parsetree.h" +#include "sepgsql/hooks.h" #include "utils/lsyscache.h" + #include "utils/syscache.h" - -@@ -305,6 +306,9 @@ create_scan_plan(PlannerInfo *root, Path *best_path) - break; +@@ -371,6 +372,16 @@ subquery_planner(PlannerGlobal *glob, Query *parse, } -+ /* Append row-level access control policy */ -+ sepgsql_rowlv_add_policy(root, (Scan *)plan); -+ /* - * If there are any pseudoconstant clauses attached to this node, insert a - * gating Result node that evaluates the pseudoconstants as one-time ++ * We set up rowlvPerms before expanding inherited tables. ++ */ ++ foreach(l, parse->rtable) ++ { ++ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); ++ ++ rte->rowlvPerms = sepgsql_rowlv_permissions(rte); ++ } ++ ++ /* + * Preprocess RowMark information. We need to do this after subquery + * pullup (so that all non-inherited RTEs are present) and before + * inheritance expansion (so that the info is available for diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index e525ba6..6474fbc 100644 --- a/src/backend/optimizer/util/clauses.c @@ -7592,7 +7954,7 @@ index b793c4d..866e0ba 100644 | LARGE_P | LAST_P diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c -index eb6505a..1962055 100644 +index 38c7e91..fbc4e9f 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -2302,8 +2302,8 @@ specialAttNum(const char *attname) @@ -7824,7 +8186,7 @@ index 90d5c76..b78f9ba 100644 cxt.ckconstraints = NIL; cxt.fkconstraints = NIL; diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c -index 98ab484..44c7a56 100644 +index aa8f2da..604a72c 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -109,6 +109,7 @@ @@ -7845,7 +8207,7 @@ index 98ab484..44c7a56 100644 /* Startup/shutdown state */ #define NoShutdown 0 -@@ -466,6 +468,7 @@ static void ShmemBackendArrayRemove(Backend *bn); +@@ -467,6 +469,7 @@ static void ShmemBackendArrayRemove(Backend *bn); #define StartBackgroundWriter() StartChildProcess(BgWriterProcess) #define StartWalWriter() StartChildProcess(WalWriterProcess) #define StartWalReceiver() StartChildProcess(WalReceiverProcess) @@ -7853,7 +8215,7 @@ index 98ab484..44c7a56 100644 /* Macros to check exit status of a child process */ #define EXIT_STATUS_0(st) ((st) == 0) -@@ -1473,6 +1476,11 @@ ServerLoop(void) +@@ -1480,6 +1483,11 @@ ServerLoop(void) if (PgStatPID == 0 && pmState == PM_RUN) PgStatPID = pgstat_start(); @@ -7865,7 +8227,7 @@ index 98ab484..44c7a56 100644 /* If we need to signal the autovacuum launcher, do so now */ if (avlauncher_needs_signal) { -@@ -2113,6 +2121,8 @@ SIGHUP_handler(SIGNAL_ARGS) +@@ -2120,6 +2128,8 @@ SIGHUP_handler(SIGNAL_ARGS) signal_child(SysLoggerPID, SIGHUP); if (PgStatPID != 0) signal_child(PgStatPID, SIGHUP); @@ -7874,17 +8236,18 @@ index 98ab484..44c7a56 100644 /* Reload authentication config files too */ if (!load_hba()) -@@ -2173,6 +2183,9 @@ pmdie(SIGNAL_ARGS) +@@ -2180,6 +2190,10 @@ pmdie(SIGNAL_ARGS) /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); + /* and the security worker too */ + if (SecWorkerPID != 0) + signal_child(SecWorkerPID, SIGTERM); - pmState = PM_WAIT_BACKUP; - } - -@@ -2223,6 +2236,9 @@ pmdie(SIGNAL_ARGS) ++ + /* + * If we're in recovery, we can't kill the startup process + * right away, because at present doing so does not release +@@ -2241,6 +2255,9 @@ pmdie(SIGNAL_ARGS) /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); @@ -7894,7 +8257,7 @@ index 98ab484..44c7a56 100644 pmState = PM_WAIT_BACKENDS; } -@@ -2258,6 +2274,8 @@ pmdie(SIGNAL_ARGS) +@@ -2276,6 +2293,8 @@ pmdie(SIGNAL_ARGS) signal_child(PgArchPID, SIGQUIT); if (PgStatPID != 0) signal_child(PgStatPID, SIGQUIT); @@ -7903,7 +8266,7 @@ index 98ab484..44c7a56 100644 ExitPostmaster(0); break; } -@@ -2529,6 +2547,16 @@ reaper(SIGNAL_ARGS) +@@ -2547,6 +2566,16 @@ reaper(SIGNAL_ARGS) continue; } @@ -7920,7 +8283,7 @@ index 98ab484..44c7a56 100644 /* * Else do standard backend child cleanup. */ -@@ -2732,6 +2760,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) +@@ -2750,6 +2779,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); } @@ -7939,7 +8302,7 @@ index 98ab484..44c7a56 100644 /* * Force a power-cycle of the pgarch process too. (This isn't absolutely * necessary, but it seems like a good idea for robustness, and it -@@ -2867,7 +2907,8 @@ PostmasterStateMachine(void) +@@ -2906,7 +2947,8 @@ PostmasterStateMachine(void) WalReceiverPID == 0 && (BgWriterPID == 0 || !FatalError) && WalWriterPID == 0 && @@ -7971,43 +8334,6 @@ index 68be146..6ba1d2f 100644 /* * No rule actions that modify OLD or NEW */ -diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c -index 25b44dd..e35f7c5 100644 ---- a/src/backend/rewrite/rewriteHandler.c -+++ b/src/backend/rewrite/rewriteHandler.c -@@ -23,6 +23,7 @@ - #include "rewrite/rewriteDefine.h" - #include "rewrite/rewriteHandler.h" - #include "rewrite/rewriteManip.h" -+#include "sepgsql/hooks.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" - #include "commands/trigger.h" -@@ -1938,6 +1939,7 @@ QueryRewrite(Query *parsetree) - foreach(l, results) - { - Query *query = (Query *) lfirst(l); -+ ListCell *cell; - - if (query->querySource == QSRC_ORIGINAL) - { -@@ -1956,7 +1958,16 @@ QueryRewrite(Query *parsetree) - query->querySource == QSRC_QUAL_INSTEAD_RULE)) - lastInstead = query; - } -+ -+ /* Fixup row-level access control permissions */ -+ foreach (cell, query->rtable) -+ { -+ RangeTblEntry *rte = lfirst(cell); -+ -+ rte->rowlvPerms = sepgsql_rowlv_permissions(rte); -+ } - } -+ sepgsql_proxy_queries(results); - - if (!foundOriginalQuery && lastInstead != NULL) - lastInstead->canSetTag = true; diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c index c1c5ce9..0f6a446 100644 --- a/src/backend/rewrite/rewriteRemove.c @@ -8032,7 +8358,7 @@ index c1c5ce9..0f6a446 100644 */ diff --git a/src/backend/sepgsql/Makefile b/src/backend/sepgsql/Makefile new file mode 100644 -index 0000000..f7119da +index 0000000..c8fb740 --- /dev/null +++ b/src/backend/sepgsql/Makefile @@ -0,0 +1,21 @@ @@ -8050,11 +8376,11 @@ index 0000000..f7119da +OBJS = dummy.o +endif + -+OBJS += proxy.o rowlv.o ++OBJS += rowlv.o + +OBJS += misc.o database.o schema.o relation.o attribute.o proc.o \ + type.o tablespace.o operator.o role.o blob.o conversion.o \ -+ tsearch.o fdw.o ++ tsearch.o fdw.o file.o + +include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/sepgsql/attribute.c b/src/backend/sepgsql/attribute.c @@ -9745,9 +10071,302 @@ index 0000000..be4a34b + } +#endif +} +diff --git a/src/backend/sepgsql/file.c b/src/backend/sepgsql/file.c +new file mode 100644 +index 0000000..9a9e8a3 +--- /dev/null ++++ b/src/backend/sepgsql/file.c +@@ -0,0 +1,287 @@ ++/* ++ * file.c ++ * ++ * SELinux hooks related to filesystem ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++ ++#include ++#ifdef HAVE_SELINUX ++#include ++#endif ++#include ++#include ++#include ++ ++#ifdef HAVE_SELINUX ++static uint16 ++sepgsql_file_class(const char *filename, bool abort) ++{ ++ struct stat stbuf; ++ uint16 tclass; ++ ++ if (stat(filename, &stbuf) < 0) ++ { ++ if (abort) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not stat file \"%s\": %m", filename))); ++ return 0; ++ } ++ ++ /* decide object class to be applied */ ++ 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; ++ ++ return tclass; ++} ++ ++static bool ++sepgsql_file_common(const char *filename, uint16 tclass, uint32 required, bool abort) ++{ ++ struct stat stbuf; ++ char *scontext = sepgsql_get_client_label(); ++ char *tcontext; ++ char *temp; ++ bool retval; ++ ++ if (stat(filename, &stbuf) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not stat file \"%s\"", filename))); ++ ++ if (getfilecon_raw(filename, &temp) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not get security context \"%s\"", filename))); ++ ++ PG_TRY(); ++ { ++ tcontext = pstrdup(temp); ++ } ++ PG_CATCH(); ++ { ++ freecon(temp); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(temp); ++ ++ retval = sepgsql_compute_perms(scontext, tcontext, ++ tclass, required, ++ filename, abort); ++ pfree(tcontext); ++ ++ return retval; ++} ++#endif ++ ++void ++sepgsql_file_getattr(const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ uint16 tclass = sepgsql_file_class(filename, true); ++ ++ sepgsql_file_common(filename, tclass, ++ SEPG_FILE__GETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_file_read(const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ uint16 tclass = sepgsql_file_class(filename, true); ++ ++ sepgsql_file_common(filename, tclass, ++ SEPG_FILE__READ, true); ++ } ++#endif ++} ++ ++void ++sepgsql_file_write(const char *filename, bool may_create) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ uint16 tclass = sepgsql_file_class(filename, !may_create); ++ char *dir_name; ++ char *tcontext; ++ char *ncontext; ++ char *temp; ++ ++ /* write an existing file */ ++ if (tclass != 0) ++ { ++ sepgsql_file_common(filename, tclass, ++ SEPG_FILE__WRITE, true); ++ return; ++ } ++ ++ /* create a new file, and write it */ ++ dir_name = dirname(pstrdup(filename)); ++ ++ if (sepgsql_file_class(dir_name, true) != SEPG_CLASS_DIR) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a directory", dir_name))); ++ ++ if (getfilecon(dir_name, &temp) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not get security context \"%s\"", dir_name))); ++ PG_TRY(); ++ { ++ tcontext = pstrdup(temp); ++ } ++ PG_CATCH(); ++ { ++ freecon(temp); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(temp); ++ ++ /* dir:{add_name} */ ++ sepgsql_file_common(dir_name, SEPG_CLASS_DIR, ++ SEPG_DIR__ADD_NAME, true); ++ ++ /* file:{create write} */ ++ ncontext = sepgsql_compute_create(sepgsql_get_client_label(), ++ tcontext, SEPG_CLASS_FILE); ++ sepgsql_compute_perms(sepgsql_get_client_label(), ++ ncontext, ++ SEPG_CLASS_FILE, ++ SEPG_FILE__CREATE | ++ SEPG_FILE__WRITE, ++ filename, true); ++ } ++#endif ++} ++ ++void ++sepgsql_file_append(const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ if (sepgsql_file_class(filename, true) != SEPG_CLASS_FILE) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a regular file", filename))); ++ ++ sepgsql_file_common(filename, SEPG_CLASS_FILE, ++ SEPG_FILE__APPEND, true); ++ } ++#endif ++} ++ ++void ++sepgsql_file_unlink(const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ uint16 tclass = sepgsql_file_class(filename, true); ++ ++ sepgsql_file_common(filename, tclass, SEPG_FILE__UNLINK, true); ++ } ++#endif ++} ++ ++void ++sepgsql_file_rename(const char *oldpath, const char *newpath) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ char *olddir = dirname(pstrdup(oldpath)); ++ char *newdir = dirname(pstrdup(newpath)); ++ uint16 tclass; ++ uint32 required; ++ ++ /* file:{setattr} on oldpath */ ++ tclass = sepgsql_file_class(oldpath, true); ++ sepgsql_file_common(oldpath, tclass, ++ SEPG_FILE__RENAME, true); ++ ++ if (sepgsql_file_class(olddir, true) != SEPG_CLASS_DIR) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a directory", olddir))); ++ ++ required = SEPG_DIR__SEARCH | SEPG_DIR__REMOVE_NAME; ++ if (strcmp(olddir, newdir) == 0) ++ { ++ /* dir:{search remove_name add_name} */ ++ required |= SEPG_DIR__ADD_NAME; ++ sepgsql_file_common(olddir, SEPG_CLASS_DIR, required, true); ++ } ++ else ++ { ++ /* dir:{search remove_name (reparent)} */ ++ if (tclass == SEPG_CLASS_DIR) ++ required |= SEPG_DIR__REPARENT; ++ sepgsql_file_common(olddir, SEPG_CLASS_DIR, required, true); ++ ++ if (sepgsql_file_class(newdir, true) != SEPG_CLASS_DIR) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a directory", newdir))); ++ ++ /* dir:{add_name} */ ++ sepgsql_file_common(newdir, SEPG_CLASS_DIR, ++ SEPG_DIR__ADD_NAME, true); ++ } ++ ++ /* file:{unlink} or dir:{DIR__RMDIR}, if necessary */ ++ tclass = sepgsql_file_class(newpath, false); ++ if (tclass == SEPG_CLASS_DIR) ++ sepgsql_file_common(newpath, tclass, SEPG_DIR__RMDIR, true); ++ else if (tclass > 0) ++ sepgsql_file_common(newpath, tclass, SEPG_FILE__UNLINK, true); ++ } ++#endif ++} ++ ++void ++sepgsql_dir_list(const char *dirname) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ uint16 tclass = sepgsql_file_class(dirname, true); ++ ++ if (tclass != SEPG_CLASS_DIR) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not directory", dirname))); ++ ++ sepgsql_file_common(dirname, tclass, SEPG_DIR__READ, true); ++ } ++#endif ++} diff --git a/src/backend/sepgsql/label.c b/src/backend/sepgsql/label.c new file mode 100644 -index 0000000..9a61902 +index 0000000..7ab355c --- /dev/null +++ b/src/backend/sepgsql/label.c @@ -0,0 +1,656 @@ @@ -10033,7 +10652,7 @@ index 0000000..9a61902 + get_namespace_name(relNsp), + get_rel_name(relOid)); + -+ return lookup_init_catalog(SEPG_CLASS_DB_TABLE, namebuf); ++ return lookup_init_catalog(SEPG_CLASS_DB_TUPLE, namebuf); +} + +static char * @@ -10409,10 +11028,10 @@ index 0000000..9a61902 +} diff --git a/src/backend/sepgsql/misc.c b/src/backend/sepgsql/misc.c new file mode 100644 -index 0000000..4f29cd7 +index 0000000..377a72b --- /dev/null +++ b/src/backend/sepgsql/misc.c -@@ -0,0 +1,123 @@ +@@ -0,0 +1,147 @@ +/* + * misc.c + * @@ -10427,6 +11046,8 @@ index 0000000..4f29cd7 +#include "miscadmin.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" ++#include "utils/builtins.h" ++#include "utils/fmgroids.h" +#include "utils/guc.h" + +#include @@ -10454,7 +11075,13 @@ index 0000000..4f29cd7 +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { -+ sepgsql_avc_switch(new_label); ++ /* ++ * NOTE: When an error happened prior to initialization of ++ * SE-PostgreSQL, NULL maight be delivered. In this case, ++ * we don't need to reset avc. ++ */ ++ if (new_label != NULL) ++ sepgsql_avc_switch(new_label); + } +#endif + return old_label; @@ -10536,6 +11163,22 @@ index 0000000..4f29cd7 + elog(FATAL, "Bug? try to launch worker process without security provider"); +} + ++Datum ++sepgsql_getcon(PG_FUNCTION_ARGS) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ char *label = sepgsql_get_client_label(); ++ ++ PG_RETURN_TEXT_P(cstring_to_text(label)); ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ PG_RETURN_NULL(); ++} diff --git a/src/backend/sepgsql/operator.c b/src/backend/sepgsql/operator.c new file mode 100644 index 0000000..7960525 @@ -11368,141 +12011,6 @@ index 0000000..1149571 + } +#endif +} -diff --git a/src/backend/sepgsql/proxy.c b/src/backend/sepgsql/proxy.c -new file mode 100644 -index 0000000..32bb57a ---- /dev/null -+++ b/src/backend/sepgsql/proxy.c -@@ -0,0 +1,129 @@ -+/* -+ * proxy.c -+ * -+ * mandatory query rewriting support -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "catalog/namespace.h" -+#include "lib/stringinfo.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/builtins.h" -+#include "utils/lsyscache.h" -+#include "tcop/tcopprot.h" -+ -+#ifdef HAVE_SELINUX -+/* -+ * If row-level access control is configured, COPY TO statement -+ * shall be rewritten to SELECT * statement. -+ */ -+static void -+sepgsql_proxy_copy_stmt(CopyStmt *stmt) -+{ -+ Oid relOid; -+ Oid namespaceId; -+ List *queries; -+ ListCell *l, *p = NULL; -+ bool with_oids = false; -+ StringInfoData qbuf; -+ -+ /* no need to do nothing */ -+ if (stmt->is_from || !stmt->relation) -+ return; -+ -+ /* obtain relaion ID */ -+ relOid = RangeVarGetRelid(stmt->relation, false); -+ -+ namespaceId = get_rel_namespace(relOid); -+ -+ /* Is there WITH OID option? */ -+retry: -+ foreach (l, stmt->options) -+ { -+ DefElem *defel = (DefElem *) lfirst(l); -+ -+ Assert(IsA(defel, DefElem)); -+ if (strcmp(defel->defname, "oids") == 0) -+ { -+ with_oids = true; -+ stmt->options = list_delete_cell(stmt->options, l, p); -+ goto retry; -+ } -+ p = l; -+ } -+ -+ /* Make a query */ -+ initStringInfo(&qbuf); -+ -+ appendStringInfo(&qbuf, "SELECT %s", with_oids ? "oid" : ""); -+ -+ if (stmt->attlist == NIL) -+ appendStringInfo(&qbuf, "%s*", with_oids ? "," : ""); -+ else -+ { -+ bool need_comma = with_oids; -+ -+ foreach (l, stmt->attlist) -+ { -+ appendStringInfo(&qbuf, "%s%s", -+ need_comma ? "," : "", -+ strVal(lfirst(l))); -+ need_comma = true; -+ } -+ } -+ -+ appendStringInfo(&qbuf, " FROM ONLY %s.%s", -+ quote_identifier(get_namespace_name(namespaceId)), -+ quote_identifier(get_rel_name(relOid))); -+ -+ queries = pg_parse_query(qbuf.data); -+ -+ Assert(list_length(queries) == 1); -+ -+ /* update CopyStmt */ -+ stmt->query = lfirst(list_head(queries)); -+ stmt->relation = NULL; -+} -+#endif -+ -+void -+sepgsql_proxy_queries(List *queryList) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ ListCell *l; -+ -+ foreach (l, queryList) -+ { -+ Query *qry = lfirst(l); -+ -+ switch (qry->commandType) -+ { -+ case CMD_SELECT: -+ break; -+ -+ case CMD_UPDATE: -+ case CMD_INSERT: -+ case CMD_DELETE: -+ /* we have no rewrite policy */ -+ break; -+ -+ case CMD_UTILITY: -+ Assert(qry->utilityStmt != NULL); -+ if (IsA(qry->utilityStmt, CopyStmt)) -+ sepgsql_proxy_copy_stmt((CopyStmt *)qry->utilityStmt); -+ break; -+ -+ default: -+ /* CMD_UNKNOWN or CMD_NOTHING */ -+ break; -+ } -+ } -+ } -+#endif -+} diff --git a/src/backend/sepgsql/relation.c b/src/backend/sepgsql/relation.c new file mode 100644 index 0000000..2604749 @@ -12478,10 +12986,10 @@ index 0000000..3c4891c +} diff --git a/src/backend/sepgsql/rowlv.c b/src/backend/sepgsql/rowlv.c new file mode 100644 -index 0000000..9ace5e4 +index 0000000..1078777 --- /dev/null +++ b/src/backend/sepgsql/rowlv.c -@@ -0,0 +1,367 @@ +@@ -0,0 +1,346 @@ +/* + * rowlv.c + * @@ -12535,105 +13043,30 @@ index 0000000..9ace5e4 + return old_mode; +} + -+void -+sepgsql_rowlv_add_policy(PlannerInfo *root, Scan *scan) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled() && scan->scanrelid > 0) -+ { -+ RangeTblEntry *rte = planner_rt_fetch(scan->scanrelid, root); -+ Form_pg_class classForm; -+ HeapTuple tuple; -+ FuncExpr *func; -+ Var *v1; /* tableoid */ -+ Var *v2; /* row reference */ -+ Const *c3; /* required permissions */ -+ Const *c4; /* abort? or filter? */ -+ bool abort; -+ bool relhassecids; -+ Oid reltype; -+ -+ if (sepgsql_rowlv_mode == SEPGSQL_ROWLV_BYPASS) -+ return; -+ -+ Assert(IsA(rte, RangeTblEntry)); -+ if (rte->rowlvPerms == 0) -+ return; -+ -+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(rte->relid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation %u", rte->relid); -+ -+ classForm = (Form_pg_class) GETSTRUCT(tuple); -+ reltype = classForm->reltype; -+ relhassecids = classForm->relhassecids; -+ -+ ReleaseSysCache(tuple); -+ -+ /* -+ * In the case when tuples have no individual security labels, -+ * sepgsql_relation_perms() checks permissions on the relation's -+ * label, as if it is security label of the whole tuples. -+ */ -+ if (!relhassecids) -+ return; -+ -+ /* 1st argument : tableoid */ -+ v1 = makeVar(scan->scanrelid, -+ TableOidAttributeNumber, -+ OIDOID, -+ -1, -+ 0); -+ -+ /* 2nd argument : whole row reference */ -+ v2 = makeVar(scan->scanrelid, -+ InvalidAttrNumber, -+ reltype, -+ -1, -+ 0); -+ -+ /* 3rd argument : required permissions */ -+ c3 = makeConst(INT4OID, -+ -1, -+ sizeof(int32), -+ Int32GetDatum(rte->rowlvPerms), -+ false, -+ true); -+ -+ /* 4th argument : abort/filter mode */ -+ abort = (sepgsql_rowlv_mode != SEPGSQL_ROWLV_FILTER); -+ c4 = makeConst(BOOLOID, -+ -1, -+ sizeof(bool), -+ BoolGetDatum(abort), -+ false, -+ true); -+ -+ /* sepgsql_tuple_perms(tableoid, , ) */ -+ func = makeFuncExpr(F_SEPGSQL_TUPLE_PERMS, -+ BOOLOID, -+ list_make4(v1, v2, c3, c4), -+ COERCE_DONTCARE); -+ -+ /* append row-level access control policy */ -+ if (abort) -+ scan->plan.qual = lappend(scan->plan.qual, func); -+ else -+ scan->plan.qual = lcons(func, scan->plan.qual); -+ } -+#endif -+} -+ +uint32 +sepgsql_rowlv_permissions(RangeTblEntry *rte) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { -+ uint32 required = 0; ++ Oid relOid = rte->relid; ++ HeapTuple reltup; ++ bool relhassecids; ++ uint32 required = 0; + -+ if (!OidIsValid(rte->relid) || -+ get_rel_relkind(rte->relid) != RELKIND_RELATION) ++ if (!OidIsValid(relOid)) ++ return 0; ++ ++ if (get_rel_relkind(relOid) != RELKIND_RELATION) ++ return 0; ++ ++ reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); ++ if (!HeapTupleIsValid(reltup)) ++ elog(ERROR, "cache lookup failed for relation %u", relOid); ++ relhassecids = ((Form_pg_class) GETSTRUCT(reltup))->relhassecids; ++ ReleaseSysCache(reltup); ++ ++ if (!relhassecids) + return 0; + + if (rte->requiredPerms & ACL_SELECT) @@ -12652,6 +13085,169 @@ index 0000000..9ace5e4 + return 0; +} + ++#ifdef HAVE_SELINUX ++static bool ++sepgsql_tuple_perms(Relation rel, HeapTuple tuple, uint32 required, bool abort) ++{ ++ Form_pg_class relForm; ++ Form_pg_attribute attForm; ++ Form_pg_type typForm; ++ sepgsql_sid_t tsid; ++ uint16 tclass; ++ bool result; ++ ++ /* object class? */ ++ switch (RelationGetRelid(rel)) ++ { ++ case DatabaseRelationId: ++ tclass = SEPG_CLASS_DB_DATABASE; ++ break; ++ ++ case NamespaceRelationId: ++ tclass = SEPG_CLASS_DB_SCHEMA; ++ break; ++ ++ case RelationRelationId: ++ relForm = (Form_pg_class) GETSTRUCT(tuple); ++ switch (relForm->relkind) ++ { ++ case RELKIND_RELATION: ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ break; ++ case RELKIND_COMPOSITE_TYPE: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ default: /* index, toast */ ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ } ++ break; ++ ++ case AttributeRelationId: ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ switch (get_rel_relkind(attForm->attrelid)) ++ { ++ case RELKIND_RELATION: ++ tclass = SEPG_CLASS_DB_COLUMN; ++ break; ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ break; ++ case RELKIND_COMPOSITE_TYPE: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ default: /* index, toast */ ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ } ++ break; ++ ++ case ProcedureRelationId: ++ tclass = SEPG_CLASS_DB_PROCEDURE; ++ break; ++ ++ case LanguageRelationId: ++ tclass = SEPG_CLASS_DB_LANGUAGE; ++ break; ++ ++ case LargeObjectMetadataRelationId: ++ tclass = SEPG_CLASS_DB_BLOB; ++ break; ++ ++ case TypeRelationId: ++ typForm = (Form_pg_type) GETSTRUCT(tuple); ++ if (!OidIsValid(typForm->typrelid)) ++ tclass = SEPG_CLASS_DB_TUPLE; ++ else ++ { ++ switch (get_rel_relkind(typForm->typrelid)) ++ { ++ case RELKIND_RELATION: ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ break; ++ case RELKIND_COMPOSITE_TYPE: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ default: /* index, toast */ ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ } ++ } ++ break; ++ ++ default: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ } ++ ++ /* do permission check */ ++ tsid.relid = RelationGetRelid(rel); ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ result = sepgsql_client_perms(tsid, tclass, required, NULL, abort); ++ ++ if (!result) ++ elog(NOTICE, "denied: oid=%u tsid(relid=%u, secid=%u) label=%s tclass=%d required=%u", HeapTupleGetOid(tuple), tsid.relid, tsid.secid, seclabelRawOutput(tsid.relid, tsid.secid), tclass, required); ++ ++ return result; ++} ++#endif ++ ++bool ++sepgsql_rowlv_quals(Relation rel, uint32 required, ++ TupleTableSlot *slot, bool abort) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ HeapTuple tuple; ++ ++ /* no permission to be checked */ ++ if (!required) ++ return true; ++ ++ /* mismatch invocation for the current rowlv mode */ ++ if ((!abort && sepgsql_rowlv_mode != SEPGSQL_ROWLV_FILTER) || ++ (abort && sepgsql_rowlv_mode != SEPGSQL_ROWLV_ABORT)) ++ return true; ++ ++ Assert(OidIsValid(RelationGetRelid(rel))); ++ ++ tuple = ExecMaterializeSlot(slot); ++ ++ return sepgsql_tuple_perms(rel, tuple, required, abort); ++ } ++#endif ++ return true; ++} ++ ++bool ++sepgsql_rowlv_copyto(Relation rel, HeapTuple tuple) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ return sepgsql_tuple_perms(rel, tuple, SEPG_DB_TUPLE__SELECT, false); ++ } ++#endif ++ return true; ++} ++ +void +sepgsql_tuple_insert(Relation rel, HeapTuple tuple) +{ @@ -12740,115 +13336,6 @@ index 0000000..9ace5e4 + } +#endif +} -+ -+Datum -+sepgsql_tuple_perms(PG_FUNCTION_ARGS) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Form_pg_class relForm; -+ Form_pg_attribute attForm; -+ sepgsql_sid_t tsid; -+ HeapTupleHeader htup; -+ HeapTupleData tuple; -+ uint16 tclass; -+ uint32 required; -+ Oid tableOid; -+ bool abort; -+ bool result; -+ -+ /* function arguments */ -+ tableOid = PG_GETARG_OID(0); -+ htup = PG_GETARG_HEAPTUPLEHEADER(1); -+ required = PG_GETARG_UINT32(2); -+ abort = PG_GETARG_BOOL(3); -+ -+ /* set up pseudo tuple */ -+ tuple.t_len = HeapTupleHeaderGetDatumLength(htup); -+ ItemPointerSetInvalid(&(tuple.t_self)); -+ tuple.t_tableOid = tableOid; -+ tuple.t_data = htup; -+ -+ /* object class? */ -+ switch (tableOid) -+ { -+ case DatabaseRelationId: -+ tclass = SEPG_CLASS_DB_DATABASE; -+ break; -+ -+ case NamespaceRelationId: -+ tclass = SEPG_CLASS_DB_SCHEMA; -+ break; -+ -+ case RelationRelationId: -+ relForm = (Form_pg_class) GETSTRUCT(&tuple); -+ switch (relForm->relkind) -+ { -+ case RELKIND_RELATION: -+ tclass = SEPG_CLASS_DB_TABLE; -+ break; -+ case RELKIND_SEQUENCE: -+ tclass = SEPG_CLASS_DB_SEQUENCE; -+ break; -+ case RELKIND_VIEW: -+ tclass = SEPG_CLASS_DB_VIEW; -+ break; -+ case RELKIND_COMPOSITE_TYPE: -+ tclass = SEPG_CLASS_DB_TUPLE; -+ break; -+ default: /* index, toast */ -+ tclass = SEPG_CLASS_DB_TABLE; -+ break; -+ } -+ break; -+ -+ case AttributeRelationId: -+ attForm = (Form_pg_attribute) GETSTRUCT(&tuple); -+ switch (get_rel_relkind(attForm->attrelid)) -+ { -+ case RELKIND_RELATION: -+ tclass = SEPG_CLASS_DB_COLUMN; -+ break; -+ case RELKIND_SEQUENCE: -+ tclass = SEPG_CLASS_DB_SEQUENCE; -+ break; -+ case RELKIND_VIEW: -+ tclass = SEPG_CLASS_DB_VIEW; -+ break; -+ case RELKIND_COMPOSITE_TYPE: -+ tclass = SEPG_CLASS_DB_TUPLE; -+ break; -+ default: /* index, toast */ -+ tclass = SEPG_CLASS_DB_TABLE; -+ break; -+ } -+ break; -+ -+ case LanguageRelationId: -+ tclass = SEPG_CLASS_DB_LANGUAGE; -+ break; -+ -+ case LargeObjectMetadataRelationId: -+ tclass = SEPG_CLASS_DB_BLOB; -+ break; -+ -+ default: -+ tclass = SEPG_CLASS_DB_TUPLE; -+ break; -+ } -+ -+ /* do permission check */ -+ tsid.relid = tableOid; -+ tsid.secid = HeapTupleGetSecid(&tuple); -+ -+ result = sepgsql_client_perms(tsid, tclass, required, NULL, abort); -+ -+ PG_RETURN_BOOL(result); -+ } -+#endif -+ PG_RETURN_BOOL(true); -+} diff --git a/src/backend/sepgsql/schema.c b/src/backend/sepgsql/schema.c new file mode 100644 index 0000000..24878f9 @@ -13030,10 +13517,10 @@ index 0000000..24878f9 +} diff --git a/src/backend/sepgsql/selinux.c b/src/backend/sepgsql/selinux.c new file mode 100644 -index 0000000..ea9c9da +index 0000000..5e40cf9 --- /dev/null +++ b/src/backend/sepgsql/selinux.c -@@ -0,0 +1,670 @@ +@@ -0,0 +1,690 @@ +/* + * src/backend/security/sepgsql/selinux.c + * Routines to communicate with SELinux. @@ -13109,6 +13596,9 @@ index 0000000..ea9c9da + { "write", SEPG_FILE__WRITE }, + { "create", SEPG_FILE__CREATE }, + { "getattr", SEPG_FILE__GETATTR }, ++ { "unlink", SEPG_FILE__UNLINK }, ++ { "rename", SEPG_FILE__RENAME }, ++ { "append", SEPG_FILE__APPEND }, + { NULL, 0UL } + } + }, @@ -13119,6 +13609,13 @@ index 0000000..ea9c9da + { "write", SEPG_DIR__WRITE }, + { "create", SEPG_DIR__CREATE }, + { "getattr", SEPG_DIR__GETATTR }, ++ { "unlink", SEPG_DIR__UNLINK }, ++ { "rename", SEPG_DIR__RENAME }, ++ { "search", SEPG_DIR__SEARCH }, ++ { "add_name", SEPG_DIR__ADD_NAME }, ++ { "remove_name", SEPG_DIR__REMOVE_NAME }, ++ { "rmdir", SEPG_DIR__RMDIR }, ++ { "reparent", SEPG_DIR__REPARENT }, + { NULL, 0UL } + } + }, @@ -13129,6 +13626,8 @@ index 0000000..ea9c9da + { "write", SEPG_LNK_FILE__WRITE }, + { "create", SEPG_LNK_FILE__CREATE }, + { "getattr", SEPG_LNK_FILE__GETATTR }, ++ { "unlink", SEPG_LNK_FILE__UNLINK }, ++ { "rename", SEPG_LNK_FILE__RENAME }, + { NULL, 0UL } + } + }, @@ -13139,6 +13638,8 @@ index 0000000..ea9c9da + { "write", SEPG_CHR_FILE__WRITE }, + { "create", SEPG_CHR_FILE__CREATE }, + { "getattr", SEPG_CHR_FILE__GETATTR }, ++ { "unlink", SEPG_CHR_FILE__UNLINK }, ++ { "rename", SEPG_CHR_FILE__RENAME }, + { NULL, 0UL } + } + }, @@ -13149,6 +13650,8 @@ index 0000000..ea9c9da + { "write", SEPG_BLK_FILE__WRITE }, + { "create", SEPG_BLK_FILE__CREATE }, + { "getattr", SEPG_BLK_FILE__GETATTR }, ++ { "unlink", SEPG_BLK_FILE__UNLINK }, ++ { "rename", SEPG_BLK_FILE__RENAME }, + { NULL, 0UL } + } + }, @@ -13159,6 +13662,8 @@ index 0000000..ea9c9da + { "write", SEPG_SOCK_FILE__WRITE }, + { "create", SEPG_SOCK_FILE__CREATE }, + { "getattr", SEPG_SOCK_FILE__GETATTR }, ++ { "unlink", SEPG_SOCK_FILE__UNLINK }, ++ { "rename", SEPG_SOCK_FILE__RENAME }, + { NULL, 0UL } + } + }, @@ -13169,6 +13674,8 @@ index 0000000..ea9c9da + { "write", SEPG_FIFO_FILE__WRITE }, + { "create", SEPG_FIFO_FILE__CREATE }, + { "getattr", SEPG_FIFO_FILE__GETATTR }, ++ { "unlink", SEPG_FIFO_FILE__UNLINK }, ++ { "rename", SEPG_FIFO_FILE__RENAME }, + { NULL, 0UL } + } + }, @@ -14761,20 +15268,6 @@ index 575fa86..eb2b5e8 100644 /* * Prepare function call info block and insert arguments. */ -diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c -index dd00b2d..ed76bca 100644 ---- a/src/backend/tcop/postgres.c -+++ b/src/backend/tcop/postgres.c -@@ -699,6 +699,9 @@ pg_rewrite_query(Query *query) - { - /* don't rewrite utilities, just dump 'em into result list */ - querytree_list = list_make1(query); -+ -+ /* SE-PostgreSQL may rewrite the query */ -+ sepgsql_proxy_queries(querytree_list); - } - else - { diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 8ad4915..54961d8 100644 --- a/src/backend/tcop/pquery.c @@ -14958,10 +15451,36 @@ index a4e0252..36d07b7 100644 if (tblspcOid == DEFAULTTABLESPACE_OID) snprintf(tblspcPath, MAXPGPATH, "base"); diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c -index e074b79..0b58f1c 100644 +index e074b79..8bb707f 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c -@@ -173,7 +173,7 @@ pg_stat_file(PG_FUNCTION_ARGS) +@@ -25,6 +25,7 @@ + #include "mb/pg_wchar.h" + #include "miscadmin.h" + #include "postmaster/syslogger.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "utils/builtins.h" + #include "utils/memutils.h" +@@ -100,6 +101,8 @@ pg_read_file(PG_FUNCTION_ARGS) + + filename = convert_and_check_filename(filename_t); + ++ sepgsql_file_read(filename); ++ + if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL) + ereport(ERROR, + (errcode_for_file_access(), +@@ -164,6 +167,8 @@ pg_stat_file(PG_FUNCTION_ARGS) + + filename = convert_and_check_filename(filename_t); + ++ sepgsql_file_getattr(filename); ++ + if (stat(filename, &fst) < 0) + ereport(ERROR, + (errcode_for_file_access(), +@@ -173,7 +178,7 @@ pg_stat_file(PG_FUNCTION_ARGS) * This record type had better match the output parameters declared for me * in pg_proc.h. */ @@ -14970,6 +15489,15 @@ index e074b79..0b58f1c 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "size", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, +@@ -236,6 +241,8 @@ pg_ls_dir(PG_FUNCTION_ARGS) + fctx = palloc(sizeof(directory_fctx)); + fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0)); + ++ sepgsql_dir_list(fctx->location); ++ + fctx->dirdesc = AllocateDir(fctx->location); + + if (!fctx->dirdesc) diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c index 07e6aab..e5c8182 100644 --- a/src/backend/utils/adt/lockfuncs.c @@ -15010,7 +15538,7 @@ index 8379407..d44655b 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0); diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c -index 9db070d..9df61c6 100644 +index 9db070d..6881976 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -30,6 +30,7 @@ @@ -15057,36 +15585,35 @@ index 9db070d..9df61c6 100644 /*---------- * The query string built is: * SELECT fk.keycols FROM ONLY relname fk -@@ -3204,6 +3220,7 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, - Relation query_rel; +@@ -3259,6 +3275,7 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan, + int spi_result; Oid save_userid; int save_sec_context; -+ int save_rowlv_mode; ++ int save_rowlv, temp_rowlv; + Datum vals[RI_MAX_NUMKEYS * 2]; + char nulls[RI_MAX_NUMKEYS * 2]; - /* - * The query is always run against the FK table except when this is an -@@ -3220,6 +3237,12 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, - GetUserIdAndSecContext(&save_userid, &save_sec_context); +@@ -3342,6 +3359,10 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan, SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner, save_sec_context | SECURITY_LOCAL_USERID_CHANGE); -+ /* -+ * When we scan FK relation, switch row-level access control mode -+ * into abort-on-violation mode, to keep referencial integrity. -+ */ -+ if (query_rel == fk_rel) -+ save_rowlv_mode = sepgsql_rowlv_set_mode(SEPGSQL_ROWLV_ABORT); - - /* Create the plan */ - qplan = SPI_prepare(querystr, nargs, argtypes); -@@ -3229,6 +3252,8 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, ++ /* Switch rowlv control mode */ ++ temp_rowlv = (detectNewRows ? SEPGSQL_ROWLV_ABORT : SEPGSQL_ROWLV_FILTER); ++ save_rowlv = sepgsql_rowlv_set_mode(temp_rowlv); ++ + /* Finally we can run the query. */ + spi_result = SPI_execute_snapshot(qplan, + vals, nulls, +@@ -3351,6 +3372,9 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan, /* Restore UID and security context */ SetUserIdAndSecContext(save_userid, save_sec_context); -+ if (query_rel == fk_rel) -+ sepgsql_rowlv_set_mode(save_rowlv_mode); - /* Save the plan if requested */ - if (cache_plan) ++ /* Restore Rowlv control mode */ ++ sepgsql_rowlv_set_mode(save_rowlv); ++ + /* Check result */ + if (spi_result < 0) + elog(ERROR, "SPI_execute_snapshot returned %d", spi_result); diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c index c837e67..1bf4eb8 100644 --- a/src/backend/utils/adt/tid.c @@ -15181,10 +15708,10 @@ index 88d8d8b..281acb0 100644 /* other cases shouldn't happen, but return NULL */ break; diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c -index 073d25a..249b4c0 100644 +index d462510..c704bc9 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c -@@ -213,7 +213,7 @@ static void write_relcache_init_file(bool shared); +@@ -216,7 +216,7 @@ static void write_relcache_init_file(bool shared); static void write_item(const void *data, Size len, FILE *fp); static void formrdesc(const char *relationName, Oid relationReltype, @@ -15193,7 +15720,7 @@ index 073d25a..249b4c0 100644 int natts, const FormData_pg_attribute *attrs); static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK); -@@ -352,7 +352,8 @@ AllocateRelationDesc(Form_pg_class relp) +@@ -355,7 +355,8 @@ AllocateRelationDesc(Form_pg_class relp) /* and allocate attribute tuple form storage */ relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, @@ -15203,7 +15730,7 @@ index 073d25a..249b4c0 100644 /* which we mark as a reference-counted tupdesc */ relation->rd_att->tdrefcount = 1; -@@ -434,6 +435,7 @@ RelationBuildTupleDesc(Relation relation) +@@ -437,6 +438,7 @@ RelationBuildTupleDesc(Relation relation) relation->rd_att->tdtypeid = relation->rd_rel->reltype; relation->rd_att->tdtypmod = -1; /* unnecessary, but... */ relation->rd_att->tdhasoid = relation->rd_rel->relhasoids; @@ -15211,7 +15738,7 @@ index 073d25a..249b4c0 100644 constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext, sizeof(TupleConstr)); -@@ -1392,7 +1394,7 @@ LookupOpclassInfo(Oid operatorClassOid, +@@ -1396,7 +1398,7 @@ LookupOpclassInfo(Oid operatorClassOid, */ static void formrdesc(const char *relationName, Oid relationReltype, @@ -15220,7 +15747,7 @@ index 073d25a..249b4c0 100644 int natts, const FormData_pg_attribute *attrs) { Relation relation; -@@ -1455,6 +1457,7 @@ formrdesc(const char *relationName, Oid relationReltype, +@@ -1459,6 +1461,7 @@ formrdesc(const char *relationName, Oid relationReltype, relation->rd_rel->reltuples = 1; relation->rd_rel->relkind = RELKIND_RELATION; relation->rd_rel->relhasoids = hasoids; @@ -15228,16 +15755,16 @@ index 073d25a..249b4c0 100644 relation->rd_rel->relnatts = (int16) natts; /* -@@ -1464,7 +1467,7 @@ formrdesc(const char *relationName, Oid relationReltype, - * because it will never be replaced. The input values must be correctly - * defined by macros in src/include/catalog/ headers. +@@ -1468,7 +1471,7 @@ formrdesc(const char *relationName, Oid relationReltype, + * because it will never be replaced. The data comes from + * src/include/catalog/ headers via genbki.pl. */ - relation->rd_att = CreateTemplateTupleDesc(natts, hasoids); + relation->rd_att = CreateTemplateTupleDesc(natts, hasoids, hassecids); relation->rd_att->tdrefcount = 1; /* mark as refcounted */ relation->rd_att->tdtypeid = relationReltype; -@@ -2527,6 +2530,7 @@ RelationBuildLocalRelation(const char *relname, +@@ -2551,6 +2554,7 @@ RelationBuildLocalRelation(const char *relname, rel->rd_rel->relkind = RELKIND_UNCATALOGED; rel->rd_rel->relhasoids = rel->rd_att->tdhasoid; @@ -15245,16 +15772,22 @@ index 073d25a..249b4c0 100644 rel->rd_rel->relnatts = natts; rel->rd_rel->reltype = InvalidOid; /* needed when bootstrapping: */ -@@ -2767,7 +2771,7 @@ RelationCacheInitializePhase2(void) +@@ -2793,11 +2797,11 @@ RelationCacheInitializePhase2(void) if (!load_relcache_init_file(true)) { formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true, - true, Natts_pg_database, Desc_pg_database); + true, true, Natts_pg_database, Desc_pg_database); + formrdesc("pg_authid", AuthIdRelation_Rowtype_Id, true, +- true, Natts_pg_authid, Desc_pg_authid); ++ true, true, Natts_pg_authid, Desc_pg_authid); + formrdesc("pg_auth_members", AuthMemRelation_Rowtype_Id, true, +- false, Natts_pg_auth_members, Desc_pg_auth_members); ++ false, false, Natts_pg_auth_members, Desc_pg_auth_members); - #define NUM_CRITICAL_SHARED_RELS 1 /* fix if you change list above */ + #define NUM_CRITICAL_SHARED_RELS 3 /* fix if you change list above */ } -@@ -2818,13 +2822,13 @@ RelationCacheInitializePhase3(void) +@@ -2848,13 +2852,13 @@ RelationCacheInitializePhase3(void) needNewCacheFile = true; formrdesc("pg_class", RelationRelation_Rowtype_Id, false, @@ -15272,7 +15805,7 @@ index 073d25a..249b4c0 100644 #define NUM_CRITICAL_LOCAL_RELS 4 /* fix if you change list above */ } -@@ -2969,6 +2973,7 @@ RelationCacheInitializePhase3(void) +@@ -3007,6 +3011,7 @@ RelationCacheInitializePhase3(void) Assert(relation->rd_att->tdtypeid == relp->reltype); Assert(relation->rd_att->tdtypmod == -1); Assert(relation->rd_att->tdhasoid == relp->relhasoids); @@ -15280,7 +15813,7 @@ index 073d25a..249b4c0 100644 ReleaseSysCache(htup); -@@ -3079,7 +3084,7 @@ load_critical_index(Oid indexoid, Oid heapoid) +@@ -3117,7 +3122,7 @@ load_critical_index(Oid indexoid, Oid heapoid) */ static TupleDesc BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, @@ -15289,7 +15822,7 @@ index 073d25a..249b4c0 100644 { TupleDesc result; MemoryContext oldcxt; -@@ -3087,7 +3092,7 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, +@@ -3125,7 +3130,7 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, oldcxt = MemoryContextSwitchTo(CacheMemoryContext); @@ -15298,7 +15831,7 @@ index 073d25a..249b4c0 100644 result->tdtypeid = RECORDOID; /* not right, but we don't care */ result->tdtypmod = -1; -@@ -3117,7 +3122,7 @@ GetPgClassDescriptor(void) +@@ -3155,7 +3160,7 @@ GetPgClassDescriptor(void) if (pgclassdesc == NULL) pgclassdesc = BuildHardcodedDescriptor(Natts_pg_class, Desc_pg_class, @@ -15307,7 +15840,7 @@ index 073d25a..249b4c0 100644 return pgclassdesc; } -@@ -3131,7 +3136,7 @@ GetPgIndexDescriptor(void) +@@ -3169,7 +3174,7 @@ GetPgIndexDescriptor(void) if (pgindexdesc == NULL) pgindexdesc = BuildHardcodedDescriptor(Natts_pg_index, Desc_pg_index, @@ -15316,7 +15849,7 @@ index 073d25a..249b4c0 100644 return pgindexdesc; } -@@ -3947,7 +3952,8 @@ load_relcache_init_file(bool shared) +@@ -3985,7 +3990,8 @@ load_relcache_init_file(bool shared) /* initialize attribute tuple forms */ rel->rd_att = CreateTemplateTupleDesc(relform->relnatts, @@ -15463,7 +15996,7 @@ index 382008c..e6eba47 100644 (AttrNumber) 1, attname, diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c -index ed99b91..2bc5127 100644 +index 179d009..4bd7314 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -37,6 +37,7 @@ @@ -15484,18 +16017,18 @@ index ed99b91..2bc5127 100644 /* * Check connection limit for this database. * -@@ -735,6 +739,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, +@@ -800,6 +804,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, /* set up ACL framework (so CheckMyDatabase can check permissions) */ initialize_acl(); + /* set up enhanced security feature */ + sepgsql_initialize(); + - /* Process pg_db_role_setting options */ - process_settings(MyDatabaseId, GetSessionUserId()); - + /* + * Re-read the 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 f198c9c..8a8d45d 100644 +index 920daeb..44fd3a9 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -31,6 +31,7 @@ @@ -15533,7 +16066,7 @@ index f198c9c..8a8d45d 100644 /* * Options for enum values stored in other modules */ -@@ -365,6 +379,7 @@ bool log_btree_build_stats = false; +@@ -366,6 +380,7 @@ bool log_btree_build_stats = false; bool check_function_bodies = true; bool default_with_oids = false; @@ -15541,7 +16074,7 @@ index f198c9c..8a8d45d 100644 bool SQL_inheritance = true; bool Password_encryption = true; -@@ -1085,6 +1100,14 @@ static struct config_bool ConfigureNamesBool[] = +@@ -1094,6 +1109,14 @@ static struct config_bool ConfigureNamesBool[] = false, NULL, NULL }, { @@ -15556,7 +16089,7 @@ index f198c9c..8a8d45d 100644 {"logging_collector", PGC_POSTMASTER, LOGGING_WHERE, gettext_noop("Start a subprocess to capture stderr output and/or csvlogs into log files."), NULL -@@ -1254,6 +1277,36 @@ static struct config_bool ConfigureNamesBool[] = +@@ -1261,6 +1284,36 @@ static struct config_bool ConfigureNamesBool[] = false, NULL, NULL }, @@ -15593,7 +16126,7 @@ index f198c9c..8a8d45d 100644 /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL -@@ -2795,7 +2848,16 @@ static struct config_enum ConfigureNamesEnum[] = +@@ -2820,7 +2873,16 @@ static struct config_enum ConfigureNamesEnum[] = &xmloption, XMLOPTION_CONTENT, xmloption_options, NULL, NULL }, @@ -15611,7 +16144,7 @@ index f198c9c..8a8d45d 100644 /* End-of-list marker */ { -@@ -6070,7 +6132,7 @@ GetPGVariableResultDesc(const char *name) +@@ -6113,7 +6175,7 @@ GetPGVariableResultDesc(const char *name) if (guc_name_compare(name, "all") == 0) { /* need a tuple descriptor representing three TEXT columns */ @@ -15620,7 +16153,7 @@ index f198c9c..8a8d45d 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", -@@ -6086,7 +6148,7 @@ GetPGVariableResultDesc(const char *name) +@@ -6129,7 +6191,7 @@ GetPGVariableResultDesc(const char *name) (void) GetConfigOptionByName(name, &varname); /* need a tuple descriptor representing a single TEXT column */ @@ -15629,7 +16162,7 @@ index f198c9c..8a8d45d 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, TEXTOID, -1, 0); } -@@ -6109,7 +6171,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest) +@@ -6152,7 +6214,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest) value = GetConfigOptionByName(name, &varname); /* need a tuple descriptor representing a single TEXT column */ @@ -15638,7 +16171,7 @@ index f198c9c..8a8d45d 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, TEXTOID, -1, 0); -@@ -6136,7 +6198,7 @@ ShowAllGUCConfig(DestReceiver *dest) +@@ -6179,7 +6241,7 @@ ShowAllGUCConfig(DestReceiver *dest) bool isnull[3] = {false, false, false}; /* need a tuple descriptor representing three TEXT columns */ @@ -15647,7 +16180,7 @@ index f198c9c..8a8d45d 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", -@@ -6531,7 +6593,7 @@ show_all_settings(PG_FUNCTION_ARGS) +@@ -6574,7 +6636,7 @@ show_all_settings(PG_FUNCTION_ARGS) * need a tuple descriptor representing NUM_PG_SETTINGS_ATTS columns * of the appropriate types */ @@ -15657,7 +16190,7 @@ index f198c9c..8a8d45d 100644 TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample -index 02f1df0..f8a27c8 100644 +index 2ed8412..10f8947 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -76,6 +76,8 @@ @@ -15756,7 +16289,7 @@ index 14fe652..40b7b40 100644 const char *defn, const char *dropStmt, const char *copyStmt, diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c -index d83f4dc..cc0cbae 100644 +index 6bbe2de..9da50b0 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -540,6 +540,8 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, @@ -17952,7 +18485,7 @@ index 425f039..0f8043d 100644 #endif /* PG_NAMESPACE_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h -index f2751a4..d7e7db8 100644 +index f2751a4..3e024b3 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3719,6 +3719,10 @@ DESCR("current user privilege on role by role name"); @@ -17960,8 +18493,8 @@ index f2751a4..d7e7db8 100644 DESCR("current user privilege on role by role oid"); +/* SE-PostgreSQL related stuff */ -+DATA(insert OID = 3405 ( sepgsql_tuple_perms PGNSP PGUID 12 0 0 0 f f f t f v 4 0 16 "26 2249 23 16" _null_ _null_ _null_ _null_ sepgsql_tuple_perms _null_ _null_ _null_ )); -+DATA(insert OID = 3406 ( seclabel_to_secid PGNSP PGUID 12 0 0 0 f f f t f v 1 0 26 "2249" _null_ _null_ _null_ _null_ seclabel_to_secid _null_ _null_ _null_ )); ++DATA(insert OID = 3405 ( 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 = 3407 ( 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 = 1269 ( pg_column_size PGNSP PGUID 12 1 0 0 f f f t f s 1 0 23 "2276" _null_ _null_ _null_ _null_ pg_column_size _null_ _null_ _null_ )); DESCR("bytes required to store the value, perhaps with compression"); @@ -18080,10 +18613,10 @@ index 5cf90af..1ca2dd4 100644 extern void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h -index 95f86d3..ef9cd9b 100644 +index fb6f678..f642ded 100644 --- a/src/include/catalog/toasting.h +++ b/src/include/catalog/toasting.h -@@ -61,5 +61,8 @@ DECLARE_TOAST(pg_shdescription, 2846, 2847); +@@ -58,5 +58,8 @@ DECLARE_TOAST(pg_shdescription, 2846, 2847); DECLARE_TOAST(pg_db_role_setting, 2966, 2967); #define PgDbRoleSettingToastTable 2966 #define PgDbRoleSettingToastIndex 2967 @@ -18309,10 +18842,10 @@ index 684aed4..2080c95 100644 diff --git a/src/include/sepgsql/hooks.h b/src/include/sepgsql/hooks.h new file mode 100644 -index 0000000..4e983f8 +index 0000000..b929e6d --- /dev/null +++ b/src/include/sepgsql/hooks.h -@@ -0,0 +1,284 @@ +@@ -0,0 +1,293 @@ +/* + * sepgsql/hooks.h + * @@ -18324,6 +18857,7 @@ index 0000000..4e983f8 +#ifndef SEPGSQL_HOOKS_H +#define SEPGSQL_HOOKS_H + ++#include "executor/tuptable.h" +#include "nodes/plannodes.h" +#include "nodes/relation.h" +#include "storage/fd.h" @@ -18341,6 +18875,8 @@ index 0000000..4e983f8 +extern bool sepgsql_worker_needed(void); +extern void sepgsql_worker_main(void); + ++extern Datum sepgsql_getcon(PG_FUNCTION_ARGS); ++ +/* + * database.c + */ @@ -18577,6 +19113,14 @@ index 0000000..4e983f8 +extern void sepgsql_user_mapping_alter(Oid umapOid); +extern void sepgsql_user_mapping_drop(Oid umapOid, bool cascade); + ++extern void sepgsql_file_getattr(const char *filename); ++extern void sepgsql_file_read(const char *filename); ++extern void sepgsql_file_write(const char *filename, bool may_create); ++extern void sepgsql_file_append(const char *filename); ++extern void sepgsql_file_unlink(const char *filename); ++extern void sepgsql_file_rename(const char *oldpath, const char *newpath); ++extern void sepgsql_dir_list(const char *dirname); ++ +/* + * row-level access controls + */ @@ -18586,23 +19130,21 @@ index 0000000..4e983f8 + +extern int sepgsql_rowlv_get_mode(void); +extern int sepgsql_rowlv_set_mode(int new_mode); -+extern void sepgsql_rowlv_add_policy(PlannerInfo *root, Scan *plan); ++extern bool sepgsql_rowlv_quals(Relation rel, uint32 required, ++ TupleTableSlot *slot, bool abort); ++extern bool sepgsql_rowlv_copyto(Relation rel, HeapTuple tuple); +extern uint32 sepgsql_rowlv_permissions(RangeTblEntry *rte); + -+extern void sepgsql_proxy_queries(List *queryList); -+ +extern void sepgsql_tuple_insert(Relation rel, HeapTuple tuple); +extern void sepgsql_tuple_update(Relation rel, ItemPointer otid, HeapTuple newtup); + -+extern Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS); -+ +#endif /* SEPGSQL_HOOKS_H */ diff --git a/src/include/sepgsql/sepgsql.h b/src/include/sepgsql/sepgsql.h new file mode 100644 -index 0000000..ce65466 +index 0000000..5fd7195 --- /dev/null +++ b/src/include/sepgsql/sepgsql.h -@@ -0,0 +1,298 @@ +@@ -0,0 +1,318 @@ +/* + * sepgsql/sepgsql.h + * @@ -18661,36 +19203,56 @@ index 0000000..ce65466 +#define SEPG_FILE__WRITE (1<<1) +#define SEPG_FILE__CREATE (1<<2) +#define SEPG_FILE__GETATTR (1<<3) ++#define SEPG_FILE__UNLINK (1<<4) ++#define SEPG_FILE__RENAME (1<<5) ++#define SEPG_FILE__APPEND (1<<6) + +#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_DIR__UNLINK (SEPG_FILE__UNLINK) ++#define SEPG_DIR__RENAME (SEPG_FILE__RENAME) ++#define SEPG_DIR__SEARCH (1<<6) ++#define SEPG_DIR__ADD_NAME (1<<7) ++#define SEPG_DIR__REMOVE_NAME (1<<8) ++#define SEPG_DIR__RMDIR (1<<9) ++#define SEPG_DIR__REPARENT (1<<10) + +#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_LNK_FILE__UNLINK (SEPG_FILE__UNLINK) ++#define SEPG_LNK_FILE__RENAME (SEPG_FILE__RENAME) + +#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_CHR_FILE__UNLINK (SEPG_FILE__UNLINK) ++#define SEPG_CHR_FILE__RENAME (SEPG_FILE__RENAME) + +#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_BLK_FILE__UNLINK (SEPG_FILE__UNLINK) ++#define SEPG_BLK_FILE__RENAME (SEPG_FILE__RENAME) + +#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_SOCK_FILE__UNLINK (SEPG_FILE__UNLINK) ++#define SEPG_SOCK_FILE__RENAME (SEPG_FILE__RENAME) + +#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_FIFO_FILE__UNLINK (SEPG_FILE__UNLINK) ++#define SEPG_FIFO_FILE__RENAME (SEPG_FILE__RENAME) + +#define SEPG_DB_DATABASE__CREATE (1<<0) +#define SEPG_DB_DATABASE__DROP (1<<1) diff --git a/sepostgresql.spec b/sepostgresql.spec index 89789d8..c018344 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -8,19 +8,19 @@ %define selinux_policy_stores targeted mls %{!?ssl:%define ssl 1} -%{!?beta:%define beta .alpha5} +%{!?beta:%define beta .beta1} Summary: Security Enhanced PostgreSQL Name: sepostgresql Version: 9.0.0 -Release: 20100404%{?beta}%{?dist} +Release: 20100511%{?beta}%{?dist} License: PostgreSQL Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ Buildroot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) #XXX - to be revert later #Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.bz2 -Source0: postgresql-9.0alpha5.tar.bz2 +Source0: postgresql-9.0beta1.tar.gz Source1: sepostgresql.init Source2: sepostgresql.8 Source3: sepostgresql.logrotate @@ -51,7 +51,7 @@ reference monitor to check any SQL query. %prep #XXX - to be revert later #%setup -q -n postgresql-%{version} -%setup -q -n postgresql-9.0alpha5 +%setup -q -n postgresql-9.0beta1 %patch0 -p1 %patch1 -p1 @@ -94,6 +94,7 @@ mv %{buildroot}%{_libdir}.orig/sepgsql/dict_snowball.so \ %{buildroot}%{_libdir}.orig/sepgsql/plpgsql.so \ %{buildroot}%{_libdir}.orig/sepgsql/*_and_*.so \ %{buildroot}%{_libdir}.orig/sepgsql/euc2004_sjis2004.so \ + %{buildroot}%{_libdir}.orig/sepgsql/libpqwalreceiver.so \ %{buildroot}%{_libdir}/sepgsql # remove unnecessary files @@ -185,6 +186,9 @@ fi %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog +* Tue May 11 2010 KaiGai Kohei - 9.0.0-20100511 +- upgrade base version to 9.0beta1 + * Sun Apr 4 2010 KaiGai Kohei - 9.0.0-20100404 - upgrade base version 8.4.3->9.0alpha5 diff --git a/sources b/sources index 130572c..4ba8bc6 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -df7a869e7a1fdbe5c5ffc3928eb3d234 postgresql-9.0alpha5.tar.bz2 +acaa0852b970524b91ea9a2fd0fa61c8 postgresql-9.0beta1.tar.gz From 3b2f53bb58597cdf67d72bbc73184815acffacee Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Mon, 24 May 2010 12:52:13 +0000 Subject: [PATCH 10/24] upgrade base version 8.4.3 -> 9.0.0beta1 --- .cvsignore | 2 +- pgsql-01-8.4-blobs.patch | 2600 ---- pgsql-02-8.4-sepgsql.patch | 20258 ----------------------------- sepostgresql-9.0-fullset.patch | 19565 ++++++++++++++++++++++++++++ sepostgresql-fedora-prefix.patch | 84 +- sepostgresql.spec | 72 +- sources | 2 +- 7 files changed, 19653 insertions(+), 22930 deletions(-) delete mode 100644 pgsql-01-8.4-blobs.patch delete mode 100644 pgsql-02-8.4-sepgsql.patch create mode 100644 sepostgresql-9.0-fullset.patch diff --git a/.cvsignore b/.cvsignore index 8fb639c..15f7b39 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1 @@ -postgresql-8.4.3.tar.bz2 +postgresql-9.0beta1.tar.gz diff --git a/pgsql-01-8.4-blobs.patch b/pgsql-01-8.4-blobs.patch deleted file mode 100644 index b2ef970..0000000 --- a/pgsql-01-8.4-blobs.patch +++ /dev/null @@ -1,2600 +0,0 @@ -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)); -+ -+ 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) - { -*************** ExecGrant_Language(InternalGrant *istmt) -*** 1782,1787 **** ---- 1809,1946 ---- - } - - 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; -*************** 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 deleted file mode 100644 index 0339b07..0000000 --- a/pgsql-02-8.4-sepgsql.patch +++ /dev/null @@ -1,20258 +0,0 @@ -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-9.0-fullset.patch b/sepostgresql-9.0-fullset.patch new file mode 100644 index 0000000..ae16554 --- /dev/null +++ b/sepostgresql-9.0-fullset.patch @@ -0,0 +1,19565 @@ +diff --git a/configure b/configure +index b91de59..b44a6be 100755 +--- a/configure ++++ b/configure +@@ -707,6 +707,7 @@ LDFLAGS_SL + ELF_SYS + EGREP + GREP ++enable_selinux + with_zlib + with_system_tzdata + with_libxslt +@@ -842,6 +843,7 @@ with_libxml + with_libxslt + with_system_tzdata + with_zlib ++enable_selinux + with_gnu_ld + enable_largefile + enable_float4_byval +@@ -1498,6 +1500,7 @@ Optional Features: + --enable-depend turn on automatic dependency tracking + --enable-cassert enable assertion checks (for debugging) + --disable-thread-safety disable thread-safety in client libraries ++ --enable-selinux build with SELinux support + --disable-largefile omit support for large files + --disable-float4-byval disable float4 passed by value + --disable-float8-byval disable float8 passed by value +@@ -5608,6 +5611,201 @@ fi + + + # ++# SELinux support ++# ++ ++ ++# Check whether --enable-selinux was given. ++if test "${enable_selinux+set}" = set; then ++ enableval=$enable_selinux; ++ case $enableval in ++ yes) ++ : ++ ;; ++ no) ++ : ++ ;; ++ *) ++ { { $as_echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5 ++$as_echo "$as_me: error: no argument expected for --enable-selinux option" >&2;} ++ { (exit 1); exit 1; }; } ++ ;; ++ esac ++ ++else ++ enable_selinux=no ++ ++fi ++ ++ ++if test "$enable_selinux" = yes; then ++ ++{ $as_echo "$as_me:$LINENO: checking for avc_open in -lselinux" >&5 ++$as_echo_n "checking for avc_open in -lselinux... " >&6; } ++if test "${ac_cv_lib_selinux_avc_open+set}" = set; then ++ $as_echo_n "(cached) " >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lselinux $LIBS" ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char avc_open (); ++int ++main () ++{ ++return avc_open (); ++ ; ++ return 0; ++} ++_ACEOF ++rm -f conftest.$ac_objext conftest$ac_exeext ++if { (ac_try="$ac_link" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" ++$as_echo "$ac_try_echo") >&5 ++ (eval "$ac_link") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest$ac_exeext && { ++ test "$cross_compiling" = yes || ++ $as_test_x conftest$ac_exeext ++ }; then ++ ac_cv_lib_selinux_avc_open=yes ++else ++ $as_echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_cv_lib_selinux_avc_open=no ++fi ++ ++rm -rf conftest.dSYM ++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_avc_open" >&5 ++$as_echo "$ac_cv_lib_selinux_avc_open" >&6; } ++if test "x$ac_cv_lib_selinux_avc_open" = x""yes; then ++ cat >>confdefs.h <<_ACEOF ++#define HAVE_LIBSELINUX 1 ++_ACEOF ++ ++ LIBS="-lselinux $LIBS" ++ ++else ++ { { $as_echo "$as_me:$LINENO: error: \"SELinux support requires libselinux.\"" >&5 ++$as_echo "$as_me: error: \"SELinux support requires libselinux.\"" >&2;} ++ { (exit 1); exit 1; }; } ++fi ++ ++ ++{ $as_echo "$as_me:$LINENO: checking for audit_open in -laudit" >&5 ++$as_echo_n "checking for audit_open in -laudit... " >&6; } ++if test "${ac_cv_lib_audit_audit_open+set}" = set; then ++ $as_echo_n "(cached) " >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-laudit $LIBS" ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char audit_open (); ++int ++main () ++{ ++return audit_open (); ++ ; ++ return 0; ++} ++_ACEOF ++rm -f conftest.$ac_objext conftest$ac_exeext ++if { (ac_try="$ac_link" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" ++$as_echo "$ac_try_echo") >&5 ++ (eval "$ac_link") 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest$ac_exeext && { ++ test "$cross_compiling" = yes || ++ $as_test_x conftest$ac_exeext ++ }; then ++ ac_cv_lib_audit_audit_open=yes ++else ++ $as_echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_cv_lib_audit_audit_open=no ++fi ++ ++rm -rf conftest.dSYM ++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_audit_audit_open" >&5 ++$as_echo "$ac_cv_lib_audit_audit_open" >&6; } ++if test "x$ac_cv_lib_audit_audit_open" = x""yes; then ++ cat >>confdefs.h <<_ACEOF ++#define HAVE_LIBAUDIT 1 ++_ACEOF ++ ++ LIBS="-laudit $LIBS" ++ ++else ++ { { $as_echo "$as_me:$LINENO: error: \"SELinux support requires libaudit.\"" >&5 ++$as_echo "$as_me: error: \"SELinux support requires libaudit.\"" >&2;} ++ { (exit 1); exit 1; }; } ++fi ++ ++ ++cat >>confdefs.h <<_ACEOF ++#define HAVE_SELINUX 1 ++_ACEOF ++ ++ ++fi ++ ++# + # Elf + # + +diff --git a/configure.in b/configure.in +index b2fa682..5797ff5 100644 +--- a/configure.in ++++ b/configure.in +@@ -755,6 +755,19 @@ PGAC_ARG_BOOL(with, zlib, yes, + AC_SUBST(with_zlib) + + # ++# SELinux support ++# ++PGAC_ARG_BOOL(enable, selinux, no, [build with SELinux support]) ++if test "$enable_selinux" = yes; then ++ AC_CHECK_LIB(selinux, avc_open,, ++ AC_MSG_ERROR("SELinux support requires libselinux.")) ++ AC_CHECK_LIB(audit, audit_open,, ++ AC_MSG_ERROR("SELinux support requires libaudit.")) ++ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, [SE-PostgreSQL feature is enabled]) ++ AC_SUBST(enable_selinux) ++fi ++ ++# + # Elf + # + +diff --git a/contrib/adminpack/adminpack.c b/contrib/adminpack/adminpack.c +index 13e04b2..c613826 100644 +--- a/contrib/adminpack/adminpack.c ++++ b/contrib/adminpack/adminpack.c +@@ -22,6 +22,7 @@ + #include "funcapi.h" + #include "miscadmin.h" + #include "postmaster/syslogger.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "utils/builtins.h" + #include "utils/datetime.h" +@@ -142,10 +143,16 @@ pg_file_write(PG_FUNCTION_ARGS) + (ERRCODE_DUPLICATE_FILE, + errmsg("file \"%s\" exists", filename))); + ++ sepgsql_file_write(filename, true); ++ + f = fopen(filename, "wb"); + } + else ++ { ++ sepgsql_file_append(filename); ++ + f = fopen(filename, "ab"); ++ } + + if (!f) + ereport(ERROR, +@@ -217,6 +224,7 @@ pg_file_rename(PG_FUNCTION_ARGS) + + if (fn3) + { ++ sepgsql_file_rename(fn2, fn3); + if (rename(fn2, fn3) != 0) + { + ereport(ERROR, +@@ -224,6 +232,7 @@ pg_file_rename(PG_FUNCTION_ARGS) + errmsg("could not rename \"%s\" to \"%s\": %m", + fn2, fn3))); + } ++ sepgsql_file_rename(fn1, fn2); + if (rename(fn1, fn2) != 0) + { + ereport(WARNING, +@@ -247,11 +256,15 @@ pg_file_rename(PG_FUNCTION_ARGS) + } + } + } +- else if (rename(fn1, fn2) != 0) ++ else + { +- ereport(ERROR, +- (errcode_for_file_access(), +- errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2))); ++ sepgsql_file_rename(fn1, fn2); ++ if (rename(fn1, fn2) != 0) ++ { ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2))); ++ } + } + + PG_RETURN_BOOL(true); +@@ -277,6 +290,8 @@ pg_file_unlink(PG_FUNCTION_ARGS) + errmsg("file \"%s\" is not accessible: %m", filename))); + } + ++ sepgsql_file_unlink(filename); ++ + if (unlink(filename) < 0) + { + ereport(WARNING, +@@ -312,11 +327,14 @@ pg_logdir_ls(PG_FUNCTION_ARGS) + TupleDesc tupdesc; + + funcctx = SRF_FIRSTCALL_INIT(); ++ ++ sepgsql_dir_list(Log_directory); ++ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + fctx = palloc(sizeof(directory_fctx)); + +- tupdesc = CreateTemplateTupleDesc(2, false); ++ tupdesc = CreateTemplateTupleDesc(2, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime", + TIMESTAMPOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename", +diff --git a/contrib/sepgtest/sepgtest b/contrib/sepgtest/sepgtest +new file mode 100755 +index 0000000..30e7cb9 +--- /dev/null ++++ b/contrib/sepgtest/sepgtest +@@ -0,0 +1,45 @@ ++#!/bin/sh ++ ++SEPGTEST=`basename $0` ++PSQL=`which psql` ++DIFF=`which diff` ++RUNCON=`which runcon` ++ ++DBUSER="" ++DBHOST="" ++DBPORT="" ++DBNAME="testdb" ++ ++usage() ++{ ++ echo "${SEPGTEST}: [-u ] [-h ] [-p ] [-d ]" ++ exit 1 ++} ++ ++# parse options ++CMD_OPTS=`getopt -n "${SEPGTEST}" "u:h:p:d:" $*` || usage ++eval set -- $CMD_OPTS ++while [ -n "$1" ]; ++do ++ case $1 in ++ -u) ++ DBUSER="-U $2"; shift 2;; ++ -h) ++ DBHOST="-h $2"; shift 2;; ++ -p) ++ DBPORT="-p $2"; shift 2;; ++ -d) ++ DBNAME="$2"; shift 2;; ++ --) ++ shift; break;; ++ *) ++ usage; shift 2;; ++ esac ++done ++ ++PSQL_OPTS="${PSQL} ${DBUSER} ${DBHOST} ${DBPORT} ${DBNAME}" ++ ++# check environment ++ ++ ++ +diff --git a/contrib/sepgtest/sepgtest.in b/contrib/sepgtest/sepgtest.in +new file mode 100755 +index 0000000..30e7cb9 +--- /dev/null ++++ b/contrib/sepgtest/sepgtest.in +@@ -0,0 +1,45 @@ ++#!/bin/sh ++ ++SEPGTEST=`basename $0` ++PSQL=`which psql` ++DIFF=`which diff` ++RUNCON=`which runcon` ++ ++DBUSER="" ++DBHOST="" ++DBPORT="" ++DBNAME="testdb" ++ ++usage() ++{ ++ echo "${SEPGTEST}: [-u ] [-h ] [-p ] [-d ]" ++ exit 1 ++} ++ ++# parse options ++CMD_OPTS=`getopt -n "${SEPGTEST}" "u:h:p:d:" $*` || usage ++eval set -- $CMD_OPTS ++while [ -n "$1" ]; ++do ++ case $1 in ++ -u) ++ DBUSER="-U $2"; shift 2;; ++ -h) ++ DBHOST="-h $2"; shift 2;; ++ -p) ++ DBPORT="-p $2"; shift 2;; ++ -d) ++ DBNAME="$2"; shift 2;; ++ --) ++ shift; break;; ++ *) ++ usage; shift 2;; ++ esac ++done ++ ++PSQL_OPTS="${PSQL} ${DBUSER} ${DBHOST} ${DBPORT} ${DBNAME}" ++ ++# check environment ++ ++ ++ +diff --git a/git-update-branches.sh b/git-update-branches.sh +new file mode 100755 +index 0000000..2f25fde +--- /dev/null ++++ b/git-update-branches.sh +@@ -0,0 +1,89 @@ ++#!/bin/sh ++ ++GIT_BIN=`which git` ++GIT_DIR=`(cd \`dirname $0\`; pwd)` ++UPSTREAM_URL="git://git.postgresql.org/git/postgresql.git" ++UPSTREAM_BRANCH="master" ++LOCAL_BRANCHES="master v9.0/sepgsql" ++RPMSOURCE=`rpm -E '%{_sourcedir}'` ++#================================================================ ++ ++cd ${GIT_DIR} ++ ++CURRENT_BRANCH=`${GIT_BIN} branch -l | grep '^*' | awk '{print $2}'` ++ ++for tree in ${LOCAL_BRANCHES} ++do ++ ${GIT_BIN} branch -lr | grep -q "origin/${tree}" || exit 1 ++ ${GIT_BIN} branch -l | grep -q "${tree}" || \ ++ ${GIT_BIN} checkout --track "origin/${tree}" || exit 1 ++done ++ ++# ======== create patches ======== ++if [ "$1" = "--patch" ]; then ++ LEFT_TREE="" ++ RIGHT_TREE="" ++ NUM=0 ++ ++ BASE_VERSION=`grep AC_INIT ${GIT_DIR}/configure.in \ ++ | head -1 \ ++ | sed -e 's/,/ /g' -e 's/\[//g' -e 's/\]//g' \ ++ | awk '{print $2}'` ++ BASE_MAJOR=`echo $BASE_VERSION | sed 's/\.[0-9]\+$//g'` ++ ++ for tree in ${LOCAL_BRANCHES} ++ do ++ LEFT_TREE=$RIGHT_TREE ++ RIGHT_TREE=$tree ++ ++ if [ -n "$LEFT_TREE" -a -n "$RIGHT_TREE" ]; then ++ tag=`basename ${RIGHT_TREE}` ++ ${GIT_BIN} diff $LEFT_TREE $RIGHT_TREE \ ++ > $RPMSOURCE/pgsql-${NUM}-${BASE_MAJOR}-${tag}.patch ++ echo "${NUM}) $RPMSOURCE/pgsql-${NUM}-${BASE_MAJOR}-${tag}.patch" ++ fi ++ NUM=`printf "%02d" \`expr $NUM + 1\`` ++ done ++ ++ exit 0 ++fi ++ ++# ======== sync remote git ======== ++ ++if echo "$1" | grep -q '^--sync-upstream=\?'; then ++ UPSTREAM_TAG=`echo "$1" | sed 's/^--sync-upstream=\?//g'` ++ if [ -n "${UPSTREAM_TAG}" ]; then ++ UPSTREAM_TAG=HEAD ++ fi ++ ++ LOCAL_MASTER=`echo "${LOCAL_BRANCHES}" | awk '{print $1}'` ++ ++ ${GIT_BIN} checkout ${LOCAL_MASTER} || exit 1 ++ ${GIT_BIN} pull ${UPSTREAM_URL} ${UPSTREAM_BRANCH} ${UPSTREAM_TAG} ++ if [ $? -ne 0 ]; then ++ ${GIT_BIN} reset HEAD ++ ${GIT_BIN} checkout . ++ ${GIT_BIN} clean -fd ++ ${GIT_BIN} pull --no-commit ${UPSTREAM_URL} ${UPSTREAM_BRANCH} ${UPSTREAM_TAG} ++ exit 2 ++ fi ++fi ++ ++LEFT_TREE="" ++for RIGHT_TREE in ${LOCAL_BRANCHES} ++do ++ if [ -n "${LEFT_TREE}" -a -n "${RIGHT_TREE}" ]; then ++ ${GIT_BIN} checkout ${RIGHT_TREE} || exit 1 ++ ${GIT_BIN} pull ${GIT_DIR} ${LEFT_TREE} || exit 1 ++ if [ $? -ne 0 ]; then ++ ${GIT_BIN} reset HEAD ++ ${GIT_BIN} checkout . ++ ${GIT_BIN} clean -fd ++ ${GIT_BIN} pull --no-commit ${GIT_DIR} ${LEFT_TREE} ++ exit 2 ++ fi ++ fi ++ LEFT_TREE=${RIGHT_TREE} ++done ++ ++${GIT_BIN} checkout ${CURRENT_BRANCH} +diff --git a/src/Makefile.global.in b/src/Makefile.global.in +index 1c38ac2..0e3bed5 100644 +--- a/src/Makefile.global.in ++++ b/src/Makefile.global.in +@@ -164,6 +164,7 @@ enable_nls = @enable_nls@ + enable_debug = @enable_debug@ + enable_dtrace = @enable_dtrace@ + enable_coverage = @enable_coverage@ ++enable_selinux = @enable_selinux@ + enable_thread_safety = @enable_thread_safety@ + + python_includespec = @python_includespec@ +diff --git a/src/backend/Makefile b/src/backend/Makefile +index 218544e..0b1dd31 100644 +--- a/src/backend/Makefile ++++ b/src/backend/Makefile +@@ -16,7 +16,7 @@ include $(top_builddir)/src/Makefile.global + + SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ + main nodes optimizer port postmaster regex replication rewrite \ +- storage tcop tsearch utils $(top_builddir)/src/timezone ++ sepgsql storage tcop tsearch utils $(top_builddir)/src/timezone + + include $(srcdir)/common.mk + +@@ -40,6 +40,11 @@ LIBS := $(filter-out -lpgport, $(LIBS)) $(LDAP_LIBS_BE) + # The backend doesn't need everything that's in LIBS, however + LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) + ++# SELinux needs libselinux and libaudit ++ifeq ($(enable_selinux), yes) ++LIBS := $(filter-out -lselinux -laudit, $(LIBS)) -lselinux -laudit ++endif ++ + ########################################################################## + + all: submake-libpgport submake-schemapg postgres $(POSTGRES_IMP) +diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c +index 6ec73f0..db0023f 100644 +--- a/src/backend/access/common/heaptuple.c ++++ b/src/backend/access/common/heaptuple.c +@@ -60,6 +60,7 @@ + #include "access/heapam.h" + #include "access/sysattr.h" + #include "access/tuptoaster.h" ++#include "catalog/pg_seclabel.h" + #include "executor/tuptable.h" + + +@@ -559,6 +560,9 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) + case TableOidAttributeNumber: + result = ObjectIdGetDatum(tup->t_tableOid); + break; ++ case SecurityLabelAttributeNumber: ++ result = seclabelSysattOutput(tup->t_tableOid, tup); ++ break; + default: + elog(ERROR, "invalid attnum: %d", attnum); + result = 0; /* keep compiler quiet */ +@@ -682,6 +686,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, + + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); ++ if (tupleDescriptor->tdhassecid) ++ len += sizeof(Oid)+1; + + hoff = len = MAXALIGN(len); /* align user data safely */ + +@@ -713,6 +719,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, + + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + td->t_infomask = HEAP_HASOID; ++ if (tupleDescriptor->tdhassecid) ++ td->t_infomask |= HEAP_HASSECID; + + heap_fill_tuple(tupleDescriptor, + values, +@@ -824,6 +832,8 @@ heap_modify_tuple(HeapTuple tuple, + newTuple->t_tableOid = tuple->t_tableOid; + if (tupleDesc->tdhasoid) + HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); ++ if (tupleDesc->tdhassecid) ++ HeapTupleSetSecid(newTuple, HeapTupleGetSecid(tuple)); + + return newTuple; + } +@@ -1434,6 +1444,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor, + + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); ++ if (tupleDescriptor->tdhassecid) ++ len += sizeof(Oid); + + hoff = len = MAXALIGN(len); /* align user data safely */ + +@@ -1455,6 +1467,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor, + + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + tuple->t_infomask = HEAP_HASOID; ++ if (tupleDescriptor->tdhassecid) ++ tuple->t_infomask |= HEAP_HASSECID; + + heap_fill_tuple(tupleDescriptor, + values, +diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c +index 9a8611f..325dbde 100644 +--- a/src/backend/access/common/tupdesc.c ++++ b/src/backend/access/common/tupdesc.c +@@ -34,7 +34,7 @@ + * caller can overwrite this if needed. + */ + TupleDesc +-CreateTemplateTupleDesc(int natts, bool hasoid) ++CreateTemplateTupleDesc(int natts, bool hasoid, bool hassecid) + { + TupleDesc desc; + char *stg; +@@ -88,6 +88,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid) + desc->tdtypeid = RECORDOID; + desc->tdtypmod = -1; + desc->tdhasoid = hasoid; ++ desc->tdhassecid = hassecid; + desc->tdrefcount = -1; /* assume not reference-counted */ + + return desc; +@@ -105,7 +106,8 @@ CreateTemplateTupleDesc(int natts, bool hasoid) + * caller can overwrite this if needed. + */ + TupleDesc +-CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs) ++CreateTupleDesc(int natts, bool hasoid, bool hassecid, ++ Form_pg_attribute *attrs) + { + TupleDesc desc; + +@@ -121,6 +123,7 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs) + desc->tdtypeid = RECORDOID; + desc->tdtypmod = -1; + desc->tdhasoid = hasoid; ++ desc->tdhassecid = hassecid; + desc->tdrefcount = -1; /* assume not reference-counted */ + + return desc; +@@ -139,7 +142,8 @@ CreateTupleDescCopy(TupleDesc tupdesc) + TupleDesc desc; + int i; + +- desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid); ++ desc = CreateTemplateTupleDesc(tupdesc->natts, ++ tupdesc->tdhasoid, tupdesc->tdhassecid); + + for (i = 0; i < desc->natts; i++) + { +@@ -150,6 +154,7 @@ CreateTupleDescCopy(TupleDesc tupdesc) + + desc->tdtypeid = tupdesc->tdtypeid; + desc->tdtypmod = tupdesc->tdtypmod; ++ desc->tdhassecid = tupdesc->tdhassecid; + + return desc; + } +@@ -166,7 +171,8 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) + TupleConstr *constr = tupdesc->constr; + int i; + +- desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid); ++ desc = CreateTemplateTupleDesc(tupdesc->natts, ++ tupdesc->tdhasoid, tupdesc->tdhassecid); + + for (i = 0; i < desc->natts; i++) + { +@@ -314,6 +320,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) + return false; + if (tupdesc1->tdhasoid != tupdesc2->tdhasoid) + return false; ++ if (tupdesc1->tdhassecid != tupdesc2->tdhassecid) ++ return false; + + for (i = 0; i < tupdesc1->natts; i++) + { +@@ -519,7 +527,7 @@ BuildDescForRelation(List *schema) + * allocate a new tuple descriptor + */ + natts = list_length(schema); +- desc = CreateTemplateTupleDesc(natts, false); ++ desc = CreateTemplateTupleDesc(natts, false, false); + has_not_null = false; + + attnum = 0; +@@ -604,7 +612,7 @@ BuildDescFromLists(List *names, List *types, List *typmods) + /* + * allocate a new tuple descriptor + */ +- desc = CreateTemplateTupleDesc(natts, false); ++ desc = CreateTemplateTupleDesc(natts, false, false); + + attnum = 0; + +diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c +index f01ed1e..4e8dfcb 100644 +--- a/src/backend/access/gin/ginutil.c ++++ b/src/backend/access/gin/ginutil.c +@@ -33,7 +33,7 @@ initGinState(GinState *state, Relation index) + + for (i = 0; i < index->rd_att->natts; i++) + { +- state->tupdesc[i] = CreateTemplateTupleDesc(2, false); ++ state->tupdesc[i] = CreateTemplateTupleDesc(2, false, false); + + TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL, + INT2OID, -1, 0); +diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c +index 759fc35..2fc282d 100644 +--- a/src/backend/access/heap/heapam.c ++++ b/src/backend/access/heap/heapam.c +@@ -52,6 +52,7 @@ + #include "access/xlogutils.h" + #include "catalog/catalog.h" + #include "catalog/namespace.h" ++#include "catalog/pg_seclabel.h" + #include "miscadmin.h" + #include "pgstat.h" + #include "storage/bufmgr.h" +@@ -1862,6 +1863,22 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid, + Assert(!(tup->t_data->t_infomask & HEAP_HASOID)); + } + ++ /* ++ * If this tuple has a capability to store its security id, but it has ++ * not been assigned yet, the default security id should be assigned. ++ * Note that this step does not apply any permission checks. All the ++ * caller of heap_insert() is trusted. ++ */ ++ if (relation->rd_rel->relhassecids) ++ { ++ if (!OidIsValid(HeapTupleGetSecid(tup))) ++ HeapTupleSetSecid(tup, seclabelGetNewSecid(relation, tup)); ++ } ++ else ++ { ++ Assert(!HeapTupleHasSecid(tup)); ++ } ++ + tup->t_data->t_infomask &= ~(HEAP_XACT_MASK); + tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); + tup->t_data->t_infomask |= HEAP_XMAX_INVALID; +@@ -2560,6 +2577,20 @@ l2: + Assert(!(newtup->t_data->t_infomask & HEAP_HASOID)); + } + ++ /* ++ * Preserve security-id, if not changed ++ */ ++ if (relation->rd_rel->relhassecids) ++ { ++ if (!OidIsValid(HeapTupleGetSecid(newtup))) ++ HeapTupleSetSecid(newtup, HeapTupleGetSecid(&oldtup)); ++ } ++ else ++ { ++ /* check there is not space for a security-id */ ++ Assert(!HeapTupleHasSecid(newtup)); ++ } ++ + newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK); + newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); + newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED); +@@ -3501,6 +3532,10 @@ heap_inplace_update(Relation relation, HeapTuple tuple) + memcpy((char *) htup + htup->t_hoff, + (char *) tuple->t_data + tuple->t_data->t_hoff, + newlen); ++ if (HeapTupleHeaderHasOid(htup)) ++ HeapTupleHeaderSetOid(htup, HeapTupleGetOid(tuple)); ++ if (HeapTupleHeaderHasSecid(htup)) ++ HeapTupleHeaderSetSecid(htup, HeapTupleGetSecid(tuple)); + + MarkBufferDirty(buffer); + +diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c +index 2af81df..18341f4 100644 +--- a/src/backend/access/heap/tuptoaster.c ++++ b/src/backend/access/heap/tuptoaster.c +@@ -591,6 +591,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, + hoff += BITMAPLEN(numAttrs); + if (newtup->t_data->t_infomask & HEAP_HASOID) + hoff += sizeof(Oid); ++ if (HeapTupleHasSecid(newtup)) ++ hoff += sizeof(Oid); + hoff = MAXALIGN(hoff); + Assert(hoff == newtup->t_data->t_hoff); + /* now convert to a limit on the tuple data size */ +@@ -868,6 +870,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, + new_len += BITMAPLEN(numAttrs); + if (olddata->t_infomask & HEAP_HASOID) + new_len += sizeof(Oid); ++ if (HeapTupleHeaderHasSecid(olddata)) ++ new_len += sizeof(Oid); + new_len = MAXALIGN(new_len); + Assert(new_len == olddata->t_hoff); + new_data_len = heap_compute_data_size(tupleDesc, +@@ -1019,6 +1023,8 @@ toast_flatten_tuple_attribute(Datum value, + new_len += BITMAPLEN(numAttrs); + if (olddata->t_infomask & HEAP_HASOID) + new_len += sizeof(Oid); ++ if (HeapTupleHeaderHasSecid(olddata)) ++ new_len += sizeof(Oid); + new_len = MAXALIGN(new_len); + Assert(new_len == olddata->t_hoff); + new_data_len = heap_compute_data_size(tupleDesc, +diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c +index d432c9d..28e7bde 100644 +--- a/src/backend/access/transam/twophase.c ++++ b/src/backend/access/transam/twophase.c +@@ -605,7 +605,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS) + + /* build tupdesc for result tuples */ + /* this had better match pg_prepared_xacts view in system_views.sql */ +- tupdesc = CreateTemplateTupleDesc(5, false); ++ tupdesc = CreateTemplateTupleDesc(5, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "transaction", + XIDOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "gid", +diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c +index b88cff2..d6636f7 100644 +--- a/src/backend/access/transam/xact.c ++++ b/src/backend/access/transam/xact.c +@@ -36,6 +36,7 @@ + #include "libpq/be-fsstubs.h" + #include "miscadmin.h" + #include "pgstat.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "storage/lmgr.h" +@@ -140,6 +141,8 @@ typedef struct TransactionStateData + int maxChildXids; /* allocated size of childXids[] */ + Oid prevUser; /* previous CurrentUserId setting */ + int prevSecContext; /* previous SecurityRestrictionContext */ ++ char *prevSecLabel; /* previous security label of client */ ++ int prevRowlvMode; /* previous row-level access control mode */ + bool prevXactReadOnly; /* entry-time xact r/o state */ + bool startedInRecovery; /* did we start in recovery? */ + struct TransactionStateData *parent; /* back link to parent */ +@@ -169,6 +172,8 @@ static TransactionStateData TopTransactionStateData = { + 0, /* allocated size of childXids[] */ + InvalidOid, /* previous CurrentUserId setting */ + 0, /* previous SecurityRestrictionContext */ ++ NULL, /* previous security label of the client */ ++ 0, /* previous row-level access control mode */ + false, /* entry-time xact r/o state */ + false, /* startedInRecovery */ + NULL /* link to parent state block */ +@@ -1658,6 +1663,10 @@ StartTransaction(void) + /* SecurityRestrictionContext should never be set outside a transaction */ + Assert(s->prevSecContext == 0); + ++ /* Save status of SELinux */ ++ s->prevSecLabel = sepgsql_get_client_label(); ++ s->prevRowlvMode = sepgsql_rowlv_get_mode(); ++ + /* + * initialize other subsystems for new transaction + */ +@@ -2162,6 +2171,10 @@ AbortTransaction(void) + */ + SetUserIdAndSecContext(s->prevUser, s->prevSecContext); + ++ /* Reset SELinux status */ ++ sepgsql_set_client_label(s->prevSecLabel); ++ sepgsql_rowlv_set_mode(s->prevRowlvMode); ++ + /* + * do abort processing + */ +@@ -4006,6 +4019,10 @@ AbortSubTransaction(void) + */ + SetUserIdAndSecContext(s->prevUser, s->prevSecContext); + ++ /* Reset SELinux status */ ++ sepgsql_set_client_label(s->prevSecLabel); ++ sepgsql_rowlv_set_mode(s->prevRowlvMode); ++ + /* + * We can skip all this stuff if the subxact failed before creating a + * ResourceOwner... +@@ -4145,6 +4162,8 @@ PushTransaction(void) + s->state = TRANS_DEFAULT; + s->blockState = TBLOCK_SUBBEGIN; + GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); ++ s->prevSecLabel = sepgsql_get_client_label(); ++ s->prevRowlvMode = sepgsql_rowlv_get_mode(); + s->prevXactReadOnly = XactReadOnly; + + CurrentTransactionState = s; +diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c +index 0b05e25..4f783e5 100644 +--- a/src/backend/access/transam/xlog.c ++++ b/src/backend/access/transam/xlog.c +@@ -8765,7 +8765,7 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS) + * Construct a tuple descriptor for the result row. This must match this + * function's pg_proc entry! + */ +- resultTupleDesc = CreateTemplateTupleDesc(2, false); ++ resultTupleDesc = CreateTemplateTupleDesc(2, false, false); + TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "file_name", + TEXTOID, -1, 0); + TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "file_offset", +diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y +index 387d43e..aecb449 100644 +--- a/src/backend/bootstrap/bootparse.y ++++ b/src/backend/bootstrap/bootparse.y +@@ -32,6 +32,7 @@ + #include "catalog/pg_authid.h" + #include "catalog/pg_class.h" + #include "catalog/pg_namespace.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_tablespace.h" + #include "catalog/toasting.h" + #include "commands/defrem.h" +@@ -187,10 +188,11 @@ Boot_CreateStmt: + TupleDesc tupdesc; + bool shared_relation; + bool mapped_relation; ++ bool hassecid = seclabelCatalogHasSysAttr($3); + + do_start(); + +- tupdesc = CreateTupleDesc(numattr, !($6), attrtypes); ++ tupdesc = CreateTupleDesc(numattr, !($6), hassecid, attrtypes); + + shared_relation = $5; + +@@ -245,7 +247,8 @@ Boot_CreateStmt: + ONCOMMIT_NOOP, + (Datum) 0, + false, +- true); ++ true, ++ NULL); + elog(DEBUG4, "relation created with oid %u", id); + } + do_end(); +diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c +index 455eac1..0590156 100644 +--- a/src/backend/bootstrap/bootstrap.c ++++ b/src/backend/bootstrap/bootstrap.c +@@ -33,6 +33,7 @@ + #include "postmaster/bgwriter.h" + #include "postmaster/walwriter.h" + #include "replication/walreceiver.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/ipc.h" + #include "storage/proc.h" +@@ -319,6 +320,9 @@ AuxiliaryProcessMain(int argc, char *argv[]) + case WalReceiverProcess: + statmsg = "wal receiver process"; + break; ++ case SecurityWorkerProcess: ++ statmsg = "security worker process"; ++ break; + default: + statmsg = "??? process"; + break; +@@ -428,6 +432,10 @@ AuxiliaryProcessMain(int argc, char *argv[]) + WalReceiverMain(); + proc_exit(1); /* should never return */ + ++ case SecurityWorkerProcess: ++ sepgsql_worker_main(); ++ proc_exit(1); /* should never return */ ++ + default: + elog(PANIC, "unrecognized process type: %d", auxType); + proc_exit(1); +@@ -482,6 +490,11 @@ BootstrapModeMain(void) + boot_yyparse(); + + /* ++ * Initial security labeling ++ */ ++ sepgsql_post_bootstraping(); ++ ++ /* + * We should now know about all mapped relations, so it's okay to write + * out the initial relation mapping files. + */ +@@ -779,6 +792,7 @@ InsertOneTuple(Oid objectid) + + tupDesc = CreateTupleDesc(numattr, + RelationGetForm(boot_reldesc)->relhasoids, ++ RelationGetForm(boot_reldesc)->relhassecids, + attrtypes); + tuple = heap_form_tuple(tupDesc, values, Nulls); + if (objectid != (Oid) 0) +diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile +index dafae3f..c2aea7a 100644 +--- a/src/backend/catalog/Makefile ++++ b/src/backend/catalog/Makefile +@@ -13,7 +13,7 @@ include $(top_builddir)/src/Makefile.global + OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ + pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ + pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ +- pg_db_role_setting.o pg_shdepend.o pg_type.o storage.o toasting.o ++ pg_db_role_setting.o pg_seclabel.o pg_shdepend.o pg_type.o storage.o toasting.o + + BKIFILES = postgres.bki postgres.description postgres.shdescription + +@@ -34,7 +34,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ + pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ + pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \ + pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ +- pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ ++ pg_seclabel.h pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ + pg_ts_parser.h pg_ts_template.h \ + pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ + pg_default_acl.h \ +diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c +index 2561d83..002a66c 100644 +--- a/src/backend/catalog/aclchk.c ++++ b/src/backend/catalog/aclchk.c +@@ -38,6 +38,7 @@ + #include "catalog/pg_operator.h" + #include "catalog/pg_opfamily.h" + #include "catalog/pg_proc.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_type.h" + #include "catalog/pg_ts_config.h" +@@ -46,6 +47,7 @@ + #include "foreign/foreign.h" + #include "miscadmin.h" + #include "parser/parse_func.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" +@@ -1499,6 +1501,10 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, + if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) + continue; + ++ /* Skip security label column, if it doesn't exist */ ++ if (curr_att == SecurityLabelAttributeNumber && !classForm->relhassecids) ++ continue; ++ + /* Views don't have any system columns at all */ + if (classForm->relkind == RELKIND_VIEW && curr_att < 0) + continue; +@@ -1608,6 +1614,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. +@@ -1869,6 +1877,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. +@@ -2063,6 +2073,8 @@ ExecGrant_Database(InternalGrant *istmt) + datId, grantorId, ACL_KIND_DATABASE, + NameStr(pg_database_tuple->datname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_database_grant(datId); + + /* + * Generate new ACL. +@@ -2188,6 +2200,8 @@ ExecGrant_Fdw(InternalGrant *istmt) + fdwid, grantorId, ACL_KIND_FDW, + NameStr(pg_fdw_tuple->fdwname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_fdw_grant(fdwid); + + /* + * Generate new ACL. +@@ -2313,6 +2327,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) + srvid, grantorId, ACL_KIND_FOREIGN_SERVER, + NameStr(pg_server_tuple->srvname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_fserver_grant(srvid); + + /* + * Generate new ACL. +@@ -2831,6 +2847,8 @@ ExecGrant_Namespace(InternalGrant *istmt) + nspid, grantorId, ACL_KIND_NAMESPACE, + NameStr(pg_namespace_tuple->nspname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_schema_grant(nspid); + + /* + * Generate new ACL. +@@ -2955,6 +2973,8 @@ ExecGrant_Tablespace(InternalGrant *istmt) + tblId, grantorId, ACL_KIND_TABLESPACE, + NameStr(pg_tablespace_tuple->spcname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_tablespace_grant(tblId); + + /* + * Generate new ACL. +diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c +index 3edfc23..b451e59 100644 +--- a/src/backend/catalog/catalog.c ++++ b/src/backend/catalog/catalog.c +@@ -32,6 +32,7 @@ + #include "catalog/pg_namespace.h" + #include "catalog/pg_pltemplate.h" + #include "catalog/pg_db_role_setting.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_shdepend.h" + #include "catalog/pg_shdescription.h" + #include "catalog/pg_tablespace.h" +@@ -306,6 +307,7 @@ IsSharedRelation(Oid relationId) + relationId == AuthMemRelationId || + relationId == DatabaseRelationId || + relationId == PLTemplateRelationId || ++ relationId == SecLabelRelationId || + relationId == SharedDescriptionRelationId || + relationId == SharedDependRelationId || + relationId == TableSpaceRelationId || +@@ -319,6 +321,8 @@ IsSharedRelation(Oid relationId) + relationId == DatabaseNameIndexId || + relationId == DatabaseOidIndexId || + relationId == PLTemplateNameIndexId || ++ relationId == SecLabelSecidIndexId || ++ relationId == SecLabelLabelIndexId || + relationId == SharedDescriptionObjIndexId || + relationId == SharedDependDependerIndexId || + relationId == SharedDependReferenceIndexId || +@@ -329,6 +333,8 @@ IsSharedRelation(Oid relationId) + /* These are their toast tables and toast indexes (see toasting.h) */ + if (relationId == PgDatabaseToastTable || + relationId == PgDatabaseToastIndex || ++ relationId == PgSecLabelToastTable || ++ relationId == PgSecLabelToastIndex || + relationId == PgShdescriptionToastTable || + relationId == PgShdescriptionToastIndex || + relationId == PgDbRoleSettingToastTable || +diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl +index 31aabda..c9fc09e 100644 +--- a/src/backend/catalog/genbki.pl ++++ b/src/backend/catalog/genbki.pl +@@ -218,7 +218,8 @@ foreach my $catname ( @{ $catalogs->{names} } ) + {cmin => 'cid'}, + {xmax => 'xid'}, + {cmax => 'cid'}, +- {tableoid => 'oid'} ++ {tableoid => 'oid'}, ++ {security_label => 'text'} + ); + foreach my $attr (@SYS_ATTRS) + { +diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c +index d848ef0..deaf2fb 100644 +--- a/src/backend/catalog/heap.c ++++ b/src/backend/catalog/heap.c +@@ -43,6 +43,7 @@ + #include "catalog/pg_constraint.h" + #include "catalog/pg_inherits.h" + #include "catalog/pg_namespace.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_statistic.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_type.h" +@@ -82,14 +83,16 @@ static void AddNewRelationTuple(Relation pg_class_desc, + Oid relowner, + char relkind, + Datum relacl, +- Datum reloptions); ++ Datum reloptions, ++ Oid *secLabels); + static Oid AddNewRelationType(const char *typeName, + Oid typeNamespace, + Oid new_rel_oid, + char new_rel_kind, + Oid ownerid, + Oid new_row_type, +- Oid new_array_type); ++ Oid new_array_type, ++ Oid securityId); + static void RelationRemoveInheritance(Oid relid); + static void StoreRelCheck(Relation rel, char *ccname, Node *expr, + bool is_local, int inhcount); +@@ -173,7 +176,16 @@ static FormData_pg_attribute a7 = { + true, 'p', 'i', true, false, false, true, 0 + }; + +-static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; ++/* ++ * Security Label system column ++ */ ++static FormData_pg_attribute a8 = { ++ 0, {"security_label"}, TEXTOID, 0, -1, ++ SecurityLabelAttributeNumber, 0, -1, -1, ++ false, 'x', 'i', true, false, false, true, 0 ++}; ++ ++static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8}; + + /* + * This function returns a Form_pg_attribute pointer for a system attribute. +@@ -181,12 +193,14 @@ static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; + * happen if there's a problem upstream. + */ + Form_pg_attribute +-SystemAttributeDefinition(AttrNumber attno, bool relhasoids) ++SystemAttributeDefinition(AttrNumber attno, bool relhasoids, bool relhassecids) + { + if (attno >= 0 || attno < -(int) lengthof(SysAtt)) + elog(ERROR, "invalid system attribute number %d", attno); + if (attno == ObjectIdAttributeNumber && !relhasoids) + elog(ERROR, "invalid system attribute number %d", attno); ++ if (attno == SecurityLabelAttributeNumber && !relhassecids) ++ elog(ERROR, "invalid system attribute number %d", attno); + return SysAtt[-attno - 1]; + } + +@@ -195,7 +209,7 @@ SystemAttributeDefinition(AttrNumber attno, bool relhasoids) + * pointer for a prototype definition. If not, return NULL. + */ + Form_pg_attribute +-SystemAttributeByName(const char *attname, bool relhasoids) ++SystemAttributeByName(const char *attname, bool relhasoids, bool relhassecids) + { + int j; + +@@ -203,16 +217,29 @@ SystemAttributeByName(const char *attname, bool relhasoids) + { + Form_pg_attribute att = SysAtt[j]; + +- if (relhasoids || att->attnum != ObjectIdAttributeNumber) +- { +- if (strcmp(NameStr(att->attname), attname) == 0) +- return att; +- } ++ if (!relhasoids && att->attnum == ObjectIdAttributeNumber) ++ continue; ++ if (!relhassecids && att->attnum == SecurityLabelAttributeNumber) ++ continue; ++ ++ if (strcmp(NameStr(att->attname), attname) == 0) ++ return att; + } + + return NULL; + } + ++/* ++ * If the given attribute is writable system attribute, it returns true. ++ */ ++bool ++SystemAttributeWritable(AttrNumber attnum, bool hasoids, bool hassecids) ++{ ++ if (hassecids && attnum == SecurityLabelAttributeNumber) ++ return true; ++ ++ return false; ++} + + /* ---------------------------------------------------------------- + * XXX END OF UGLY HARD CODED BADNESS XXX +@@ -391,7 +418,8 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind, + for (i = 0; i < natts; i++) + { + if (SystemAttributeByName(NameStr(tupdesc->attrs[i]->attname), +- tupdesc->tdhasoid) != NULL) ++ tupdesc->tdhasoid, ++ tupdesc->tdhassecid) != NULL) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_COLUMN), + errmsg("column name \"%s\" conflicts with a system column name", +@@ -509,7 +537,8 @@ CheckAttributeType(const char *attname, Oid atttypid, + void + InsertPgAttributeTuple(Relation pg_attribute_rel, + Form_pg_attribute new_attribute, +- CatalogIndexState indstate) ++ CatalogIndexState indstate, ++ Oid securityId) + { + Datum values[Natts_pg_attribute]; + bool nulls[Natts_pg_attribute]; +@@ -542,6 +571,8 @@ InsertPgAttributeTuple(Relation pg_attribute_rel, + nulls[Anum_pg_attribute_attoptions - 1] = true; + + tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, securityId); + + /* finally insert the new tuple, update the indexes, and clean up */ + simple_heap_insert(pg_attribute_rel, tup); +@@ -566,13 +597,15 @@ AddNewAttributeTuples(Oid new_rel_oid, + TupleDesc tupdesc, + char relkind, + bool oidislocal, +- int oidinhcount) ++ int oidinhcount, ++ Oid *secLabels) + { + Form_pg_attribute attr; + int i; + Relation rel; + CatalogIndexState indstate; + int natts = tupdesc->natts; ++ Oid secid; + ObjectAddress myself, + referenced; + +@@ -596,7 +629,10 @@ AddNewAttributeTuples(Oid new_rel_oid, + attr->attstattarget = -1; + attr->attcacheoff = -1; + +- InsertPgAttributeTuple(rel, attr, indstate); ++ secid = (!secLabels ? InvalidOid : ++ secLabels[i - FirstLowInvalidHeapAttributeNumber]); ++ ++ InsertPgAttributeTuple(rel, attr, indstate, secid); + + /* Add dependency info */ + myself.classId = RelationRelationId; +@@ -624,6 +660,11 @@ AddNewAttributeTuples(Oid new_rel_oid, + SysAtt[i]->attnum == ObjectIdAttributeNumber) + continue; + ++ /* skip security label where appropriate */ ++ if (!tupdesc->tdhassecid && ++ SysAtt[i]->attnum == SecurityLabelAttributeNumber) ++ continue; ++ + memcpy(&attStruct, (char *) SysAtt[i], sizeof(FormData_pg_attribute)); + + /* Fill in the correct relation OID in the copied tuple */ +@@ -636,7 +677,10 @@ AddNewAttributeTuples(Oid new_rel_oid, + attStruct.attinhcount = oidinhcount; + } + +- InsertPgAttributeTuple(rel, &attStruct, indstate); ++ secid = (!secLabels ? InvalidOid ++ : secLabels[SysAtt[i]->attnum - FirstLowInvalidHeapAttributeNumber]); ++ ++ InsertPgAttributeTuple(rel, &attStruct, indstate, secid); + } + } + +@@ -666,7 +710,8 @@ InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, + Datum relacl, +- Datum reloptions) ++ Datum reloptions, ++ Oid securityId) + { + Form_pg_class rd_rel = new_rel_desc->rd_rel; + Datum values[Natts_pg_class]; +@@ -696,6 +741,7 @@ InsertPgClassTuple(Relation pg_class_desc, + values[Anum_pg_class_relnatts - 1] = Int16GetDatum(rd_rel->relnatts); + values[Anum_pg_class_relchecks - 1] = Int16GetDatum(rd_rel->relchecks); + values[Anum_pg_class_relhasoids - 1] = BoolGetDatum(rd_rel->relhasoids); ++ values[Anum_pg_class_relhassecids - 1] = BoolGetDatum(rd_rel->relhassecids); + values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey); + values[Anum_pg_class_relhasexclusion - 1] = BoolGetDatum(rd_rel->relhasexclusion); + values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules); +@@ -719,6 +765,9 @@ InsertPgClassTuple(Relation pg_class_desc, + */ + HeapTupleSetOid(tup, new_rel_oid); + ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, securityId); ++ + /* finally insert the new tuple, update the indexes, and clean up */ + simple_heap_insert(pg_class_desc, tup); + +@@ -743,9 +792,11 @@ AddNewRelationTuple(Relation pg_class_desc, + Oid relowner, + char relkind, + Datum relacl, +- Datum reloptions) ++ Datum reloptions, ++ Oid *secLabels) + { + Form_pg_class new_rel_reltup; ++ Oid secid; + + /* + * first we update some of the information in our uncataloged relation's +@@ -803,9 +854,11 @@ AddNewRelationTuple(Relation pg_class_desc, + + new_rel_desc->rd_att->tdtypeid = new_type_oid; + ++ secid = (!secLabels ? InvalidOid : secLabels[0]); ++ + /* Now build and insert the tuple */ + InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, +- relacl, reloptions); ++ relacl, reloptions, secid); + } + + +@@ -822,7 +875,8 @@ AddNewRelationType(const char *typeName, + char new_rel_kind, + Oid ownerid, + Oid new_row_type, +- Oid new_array_type) ++ Oid new_array_type, ++ Oid securityId) + { + return + TypeCreate(new_row_type, /* optional predetermined OID */ +@@ -854,7 +908,8 @@ AddNewRelationType(const char *typeName, + 'x', /* fully TOASTable */ + -1, /* typmod */ + 0, /* array dimensions for typBaseType */ +- false); /* Type NOT NULL */ ++ false, /* Type NOT NULL */ ++ securityId); /* security-id of the type */ + } + + /* -------------------------------- +@@ -903,7 +958,8 @@ heap_create_with_catalog(const char *relname, + OnCommitAction oncommit, + Datum reloptions, + bool use_user_acl, +- bool allow_system_table_mods) ++ bool allow_system_table_mods, ++ Oid *secLabels) + { + Relation pg_class_desc; + Relation new_rel_desc; +@@ -911,6 +967,7 @@ heap_create_with_catalog(const char *relname, + Oid old_type_oid; + Oid new_type_oid; + Oid new_array_oid = InvalidOid; ++ Oid type_secid = InvalidOid; + + pg_class_desc = heap_open(RelationRelationId, RowExclusiveLock); + +@@ -1030,6 +1087,11 @@ heap_create_with_catalog(const char *relname, + relkind == RELKIND_COMPOSITE_TYPE)) + new_array_oid = AssignTypeArrayOid(); + ++ /* security context of the relation type */ ++ if (secLabels) ++ type_secid = seclabelMoveSecid(TypeRelationId, ++ RelationRelationId, secLabels[0]); ++ + /* + * Since defining a relation also defines a complex type, we add a new + * system type corresponding to the new relation. The OID of the type can +@@ -1046,7 +1108,8 @@ heap_create_with_catalog(const char *relname, + relkind, + ownerid, + reltypeid, +- new_array_oid); ++ new_array_oid, ++ type_secid); + + /* + * Now make the array type if wanted. +@@ -1086,7 +1149,8 @@ heap_create_with_catalog(const char *relname, + 'x', /* fully TOASTable */ + -1, /* typmod */ + 0, /* array dimensions for typBaseType */ +- false); /* Type NOT NULL */ ++ false, /* Type NOT NULL */ ++ type_secid); /* security-id of the type */ + + pfree(relarrayname); + } +@@ -1106,13 +1170,14 @@ heap_create_with_catalog(const char *relname, + ownerid, + relkind, + PointerGetDatum(relacl), +- reloptions); ++ reloptions, ++ secLabels); + + /* + * now add tuples to pg_attribute for the attributes in our new relation. + */ + AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, +- oidislocal, oidinhcount); ++ oidislocal, oidinhcount, secLabels); + + /* + * Make a dependency link to force the relation to be deleted if its +@@ -1590,6 +1655,11 @@ heap_drop_with_catalog(Oid relid) + * delete relation tuple + */ + DeleteRelationTuple(relid); ++ ++ /* ++ * delete orphan pg_seclabel entries ++ */ ++ seclabelOnDropTable(relid); + } + + +diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c +index 69946fe..23fb19b 100644 +--- a/src/backend/catalog/index.c ++++ b/src/backend/catalog/index.c +@@ -39,6 +39,7 @@ + #include "catalog/pg_constraint.h" + #include "catalog/pg_operator.h" + #include "catalog/pg_opclass.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_trigger.h" + #include "catalog/pg_type.h" +@@ -90,7 +91,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, + Oid *classObjectId); + static void InitializeAttributeOids(Relation indexRelation, + int numatts, Oid indexoid); +-static void AppendAttributeTuples(Relation indexRelation, int numatts); ++static void AppendAttributeTuples(Relation indexRelation, ++ int numatts, Oid securityId); + static void UpdateIndexRelation(Oid indexoid, Oid heapoid, + IndexInfo *indexInfo, + Oid *classOids, +@@ -155,7 +157,7 @@ ConstructTupleDescriptor(Relation heapRelation, + /* + * allocate the new tuple descriptor + */ +- indexTupDesc = CreateTemplateTupleDesc(numatts, false); ++ indexTupDesc = CreateTemplateTupleDesc(numatts, false, false); + + /* + * For simple index columns, we copy the pg_attribute row from the parent +@@ -182,7 +184,8 @@ ConstructTupleDescriptor(Relation heapRelation, + * here we are indexing on a system attribute (-1...-n) + */ + from = SystemAttributeDefinition(atnum, +- heapRelation->rd_rel->relhasoids); ++ heapRelation->rd_rel->relhasoids, ++ heapRelation->rd_rel->relhassecids); + } + else + { +@@ -339,13 +342,16 @@ InitializeAttributeOids(Relation indexRelation, + * ---------------------------------------------------------------- + */ + static void +-AppendAttributeTuples(Relation indexRelation, int numatts) ++AppendAttributeTuples(Relation indexRelation, int numatts, Oid securityId) + { + Relation pg_attribute; + CatalogIndexState indstate; + TupleDesc indexTupDesc; + int i; + ++ /* copy security id */ ++ securityId = seclabelMoveSecid(AttributeRelationId, ++ RelationRelationId, securityId); + /* + * open the attribute relation and its indexes + */ +@@ -367,7 +373,8 @@ AppendAttributeTuples(Relation indexRelation, int numatts) + Assert(indexTupDesc->attrs[i]->attnum == i + 1); + Assert(indexTupDesc->attrs[i]->attcacheoff == -1); + +- InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate); ++ InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], ++ indstate, securityId); + } + + CatalogCloseIndexes(indstate); +@@ -545,6 +552,7 @@ index_create(Oid heapRelationId, + bool mapped_relation; + bool is_exclusion; + Oid namespaceId; ++ Oid securityId; + int i; + + is_exclusion = (indexInfo->ii_ExclusionOps != NULL); +@@ -682,15 +690,21 @@ index_create(Oid heapRelationId, + indexRelation->rd_rel->relam = accessMethodObjectId; + indexRelation->rd_rel->relkind = RELKIND_INDEX; + indexRelation->rd_rel->relhasoids = false; ++ indexRelation->rd_rel->relhassecids = false; + indexRelation->rd_rel->relhasexclusion = is_exclusion; + + /* ++ * Index always has same security id of the relation to be indexed on. ++ */ ++ securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(heapRelationId)); ++ ++ /* + * store index's pg_class entry + */ + InsertPgClassTuple(pg_class, indexRelation, + RelationGetRelid(indexRelation), + (Datum) 0, +- reloptions); ++ reloptions, securityId); + + /* done with pg_class */ + heap_close(pg_class, RowExclusiveLock); +@@ -706,7 +720,7 @@ index_create(Oid heapRelationId, + /* + * append ATTRIBUTE tuples for the index + */ +- AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs); ++ AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs, securityId); + + /* ---------------- + * update pg_index +diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c +index 5581346..cf1910a 100644 +--- a/src/backend/catalog/namespace.c ++++ b/src/backend/catalog/namespace.c +@@ -40,6 +40,7 @@ + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" ++#include "sepgsql/hooks.h" + #include "storage/backendid.h" + #include "storage/ipc.h" + #include "utils/acl.h" +@@ -2311,6 +2312,9 @@ LookupExplicitNamespace(const char *nspname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + nspname); + ++ /* SELinux checks */ ++ sepgsql_schema_search(namespaceId, true); ++ + return namespaceId; + } + +@@ -2903,7 +2907,8 @@ recomputeNamespacePath(void) + if (OidIsValid(namespaceId) && + !list_member_oid(oidlist, namespaceId) && + pg_namespace_aclcheck(namespaceId, roleid, +- ACL_USAGE) == ACLCHECK_OK) ++ ACL_USAGE) == ACLCHECK_OK && ++ sepgsql_schema_search(namespaceId, false)) + oidlist = lappend_oid(oidlist, namespaceId); + } + } +@@ -2930,7 +2935,8 @@ recomputeNamespacePath(void) + if (OidIsValid(namespaceId) && + !list_member_oid(oidlist, namespaceId) && + pg_namespace_aclcheck(namespaceId, roleid, +- ACL_USAGE) == ACLCHECK_OK) ++ ACL_USAGE) == ACLCHECK_OK && ++ sepgsql_schema_search(namespaceId, false)) + oidlist = lappend_oid(oidlist, namespaceId); + } + } +@@ -2996,9 +3002,12 @@ InitTempTableNamespace(void) + char namespaceName[NAMEDATALEN]; + Oid namespaceId; + Oid toastspaceId; ++ Oid secid; + + Assert(!OidIsValid(myTempNamespace)); + ++ snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); ++ + /* + * First, do permission check to see if we are authorized to make temp + * tables. We use a nonstandard error message here since "databasename: +@@ -3016,6 +3025,9 @@ InitTempTableNamespace(void) + errmsg("permission denied to create temporary tables in database \"%s\"", + get_database_name(MyDatabaseId)))); + ++ /* SELinux checks */ ++ secid = sepgsql_schema_create(namespaceName, true); ++ + /* + * Do not allow a Hot Standby slave session to make temp tables. Aside + * from problems with modifying the system catalogs, there is a naming +@@ -3031,8 +3043,6 @@ InitTempTableNamespace(void) + (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), + errmsg("cannot create temporary tables during recovery"))); + +- snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); +- + namespaceId = GetSysCacheOid1(NAMESPACENAME, + CStringGetDatum(namespaceName)); + if (!OidIsValid(namespaceId)) +@@ -3045,7 +3055,9 @@ InitTempTableNamespace(void) + * temp tables. This works because the places that access the temp + * namespace for my own backend skip permissions checks on it. + */ +- namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); ++ namespaceId = NamespaceCreate(namespaceName, ++ BOOTSTRAP_SUPERUSERID, ++ secid); + /* Advance command counter to make namespace visible */ + CommandCounterIncrement(); + } +@@ -3070,7 +3082,9 @@ InitTempTableNamespace(void) + CStringGetDatum(namespaceName)); + if (!OidIsValid(toastspaceId)) + { +- toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); ++ toastspaceId = NamespaceCreate(namespaceName, ++ BOOTSTRAP_SUPERUSERID, ++ secid); + /* Advance command counter to make namespace visible */ + CommandCounterIncrement(); + } +diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c +index 582d894..d7b6bd1 100644 +--- a/src/backend/catalog/pg_aggregate.c ++++ b/src/backend/catalog/pg_aggregate.c +@@ -27,6 +27,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_func.h" + #include "parser/parse_oper.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -67,6 +68,7 @@ AggregateCreate(const char *aggName, + Oid *fnArgs; + int nargs_transfn; + Oid procOid; ++ Oid procSecid; + TupleDesc tupDesc; + int i; + ObjectAddress myself, +@@ -161,6 +163,10 @@ AggregateCreate(const char *aggName, + } + Assert(OidIsValid(finaltype)); + ++ /* SELinux checks */ ++ procSecid = sepgsql_aggregate_create(aggName, aggNamespace, ++ transfn, finalfn); ++ + /* + * If finaltype (i.e. aggregate return type) is polymorphic, inputs must + * be polymorphic also, else parser will fail to deduce result type. +@@ -229,7 +235,8 @@ AggregateCreate(const char *aggName, + NIL, /* parameterDefaults */ + PointerGetDatum(NULL), /* proconfig */ + 1, /* procost */ +- 0); /* prorows */ ++ 0, /* prorows */ ++ procSecid); /* security-id */ + + /* + * Okay to create the pg_aggregate entry. +diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c +index 99085c9..b4c0b3a 100644 +--- a/src/backend/catalog/pg_conversion.c ++++ b/src/backend/catalog/pg_conversion.c +@@ -40,7 +40,7 @@ Oid + ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +- Oid conproc, bool def) ++ Oid conproc, bool def, Oid securityId) + { + int i; + Relation rel; +@@ -104,6 +104,8 @@ ConversionCreate(const char *conname, Oid connamespace, + + tup = heap_form_tuple(tupDesc, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + /* insert a new tuple */ + oid = simple_heap_insert(rel, tup); + Assert(OidIsValid(oid)); +diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c +index e3f18bf..572ce01 100644 +--- a/src/backend/catalog/pg_largeobject.c ++++ b/src/backend/catalog/pg_largeobject.c +@@ -21,10 +21,13 @@ + #include "catalog/dependency.h" + #include "catalog/indexing.h" + #include "catalog/pg_authid.h" ++#include "catalog/pg_description.h" + #include "catalog/pg_largeobject.h" + #include "catalog/pg_largeobject_metadata.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/toasting.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/bytea.h" + #include "utils/fmgroids.h" +@@ -40,7 +43,7 @@ + * will appear to exist with size 0. + */ + Oid +-LargeObjectCreate(Oid loid) ++LargeObjectCreate(Oid loid, Oid securityId) + { + Relation pg_lo_meta; + HeapTuple ntup; +@@ -66,6 +69,8 @@ LargeObjectCreate(Oid loid) + if (OidIsValid(loid)) + HeapTupleSetOid(ntup, loid); + ++ HeapTupleSetSecid(ntup, securityId); ++ + loid_new = simple_heap_insert(pg_lo_meta, ntup); + Assert(!OidIsValid(loid) || loid == loid_new); + +@@ -245,6 +250,64 @@ LargeObjectAlterOwner(Oid loid, Oid newOwnerId) + } + + /* ++ * LargeObjectAlterSecLabel ++ * ++ * Implementation of ALTER LARGE OBJECT xxx SECURITY LABEL ++ */ ++void ++LargeObjectAlterSecLabel(Oid loid, char *new_label) ++{ ++ Relation pg_lo_meta; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ Oid securityId; ++ ++ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, ++ RowExclusiveLock); ++ ++ ScanKeyInit(&skey, ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(loid)); ++ ++ scan = systable_beginscan(pg_lo_meta, ++ LargeObjectMetadataOidIndexId, true, ++ SnapshotNow, 1, &skey); ++ ++ oldtup = systable_getnext(scan); ++ if (!HeapTupleIsValid(oldtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("large object %u does not exist", loid))); ++ ++ /* Must be owner of the large object */ ++ if (!pg_largeobject_ownercheck(loid, GetUserId())) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("must be owner of large object %u", loid))); ++ ++ /* SELinux checks */ ++ securityId = sepgsql_largeobject_relabel(loid, new_label); ++ ++ /* update the tuple */ ++ newtup = heap_copytuple(oldtup); ++ ++ HeapTupleSetSecid(newtup, securityId); ++ ++ simple_heap_update(pg_lo_meta, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(pg_lo_meta, newtup); ++ ++ heap_freetuple(newtup); ++ ++ systable_endscan(scan); ++ ++ heap_close(pg_lo_meta, RowExclusiveLock); ++} ++ ++/* + * LargeObjectExists + * + * We don't use the system cache to for large object metadata, for fear of +diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c +index 22111a3..cba7274 100644 +--- a/src/backend/catalog/pg_namespace.c ++++ b/src/backend/catalog/pg_namespace.c +@@ -28,7 +28,7 @@ + * --------------- + */ + Oid +-NamespaceCreate(const char *nspName, Oid ownerId) ++NamespaceCreate(const char *nspName, Oid ownerId, Oid secid) + { + Relation nspdesc; + HeapTuple tup; +@@ -64,6 +64,8 @@ NamespaceCreate(const char *nspName, Oid ownerId) + tupDesc = nspdesc->rd_att; + + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + nspoid = simple_heap_insert(nspdesc, tup); + Assert(OidIsValid(nspoid)); +diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c +index 2362268..f6df55e 100644 +--- a/src/backend/catalog/pg_operator.c ++++ b/src/backend/catalog/pg_operator.c +@@ -28,6 +28,7 @@ + #include "catalog/pg_type.h" + #include "miscadmin.h" + #include "parser/parse_oper.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -204,6 +205,7 @@ OperatorShellMake(const char *operatorName, + { + Relation pg_operator_desc; + Oid operatorObjectId; ++ Oid securityId; + int i; + HeapTuple tup; + Datum values[Natts_pg_operator]; +@@ -220,6 +222,12 @@ OperatorShellMake(const char *operatorName, + errmsg("\"%s\" is not a valid operator name", + operatorName))); + ++ /* SELinux checks */ ++ securityId = sepgsql_operator_create(operatorName, InvalidOid, ++ operatorNamespace, ++ InvalidOid, InvalidOid, InvalidOid, ++ InvalidOid, InvalidOid); ++ + /* + * initialize our *nulls and *values arrays + */ +@@ -261,6 +269,8 @@ OperatorShellMake(const char *operatorName, + */ + tup = heap_form_tuple(tupDesc, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + /* + * insert our "shell" operator tuple + */ +@@ -340,6 +350,7 @@ OperatorCreate(const char *operatorName, + bool replaces[Natts_pg_operator]; + Datum values[Natts_pg_operator]; + Oid operatorObjectId; ++ Oid securityId; + bool operatorAlreadyDefined; + Oid operResultType; + Oid commutatorId, +@@ -476,6 +487,12 @@ OperatorCreate(const char *operatorName, + else + negatorId = InvalidOid; + ++ /* SELinux checks */ ++ securityId = sepgsql_operator_create(operatorName, operatorObjectId, ++ operatorNamespace, ++ procedureId, restrictionId, joinId, ++ commutatorId, negatorId); ++ + /* + * set up values in the operator tuple + */ +@@ -516,6 +533,8 @@ OperatorCreate(const char *operatorName, + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for operator %u", + operatorObjectId); ++ if (securityId != HeapTupleHasSecid(tup)) ++ elog(ERROR, "Bug? security-id was mismatched."); + + tup = heap_modify_tuple(tup, + RelationGetDescr(pg_operator_desc), +@@ -530,6 +549,8 @@ OperatorCreate(const char *operatorName, + tupDesc = pg_operator_desc->rd_att; + tup = heap_form_tuple(tupDesc, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + operatorObjectId = simple_heap_insert(pg_operator_desc, tup); + } + +diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c +index d76e415..f4c74a3 100644 +--- a/src/backend/catalog/pg_proc.c ++++ b/src/backend/catalog/pg_proc.c +@@ -84,7 +84,8 @@ ProcedureCreate(const char *procedureName, + List *parameterDefaults, + Datum proconfig, + float4 procost, +- float4 prorows) ++ float4 prorows, ++ Oid prosecid) + { + Oid retval; + int parameterCount; +@@ -364,6 +365,8 @@ ProcedureCreate(const char *procedureName, + if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), proowner)) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + procedureName); ++ if (prosecid != HeapTupleGetSecid(oldtup)) ++ elog(ERROR, "Bug? security-id was tried to be changed."); + + /* + * Not okay to change the return type of the existing proc, since +@@ -548,6 +551,9 @@ ProcedureCreate(const char *procedureName, + nulls[Anum_pg_proc_proacl - 1] = true; + + tup = heap_form_tuple(tupDesc, values, nulls); ++ ++ HeapTupleSetSecid(tup, prosecid); ++ + simple_heap_insert(rel, tup); + is_update = false; + } +diff --git a/src/backend/catalog/pg_seclabel.c b/src/backend/catalog/pg_seclabel.c +new file mode 100644 +index 0000000..4816635 +--- /dev/null ++++ b/src/backend/catalog/pg_seclabel.c +@@ -0,0 +1,646 @@ ++/* ++ * pg_seclabel.c ++ * ++ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#include "postgres.h" ++ ++#include "access/genam.h" ++#include "access/heapam.h" ++#include "access/sysattr.h" ++#include "access/tupdesc.h" ++#include "catalog/catalog.h" ++#include "catalog/indexing.h" ++#include "catalog/pg_aggregate.h" ++#include "catalog/pg_amop.h" ++#include "catalog/pg_amproc.h" ++#include "catalog/pg_attrdef.h" ++#include "catalog/pg_auth_members.h" ++#include "catalog/pg_constraint.h" ++#include "catalog/pg_db_role_setting.h" ++#include "catalog/pg_depend.h" ++#include "catalog/pg_description.h" ++#include "catalog/pg_enum.h" ++#include "catalog/pg_index.h" ++#include "catalog/pg_inherits.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_rewrite.h" ++#include "catalog/pg_seclabel.h" ++#include "catalog/pg_shdepend.h" ++#include "catalog/pg_shdescription.h" ++#include "catalog/pg_trigger.h" ++#include "catalog/pg_ts_config_map.h" ++#include "executor/spi.h" ++#include "miscadmin.h" ++#include "sepgsql/sepgsql.h" ++#include "sepgsql/hooks.h" ++#include "utils/builtins.h" ++#include "utils/fmgroids.h" ++#include "utils/lsyscache.h" ++#include "utils/rel.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++ ++bool ignore_security_label_input; ++ ++void ++seclabelOnCreateDatabase(Oid src_datid, Oid dst_datid) ++{ ++ Relation rel; ++ ScanKeyData keys[1]; ++ SysScanDesc scan; ++ HeapTuple oldtup, newtup; ++ Datum values[Natts_pg_seclabel]; ++ bool nulls[Natts_pg_seclabel]; ++ bool replaces[Natts_pg_seclabel]; ++ ++ /* Scan all entries with pg_seclabel.datid = src_datid */ ++ ScanKeyInit(&keys[0], ++ Anum_pg_seclabel_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(src_datid)); ++ ++ rel = heap_open(SecLabelRelationId, RowExclusiveLock); ++ ++ scan = systable_beginscan(rel, SecLabelSecidIndexId, true, ++ SnapshotNow, 1, keys); ++ ++ /* corresponding entries will be inserted with new datid */ ++ memset(values, 0, sizeof(values)); ++ memset(nulls, false, sizeof(nulls)); ++ memset(replaces, false, sizeof(replaces)); ++ ++ values[Anum_pg_seclabel_datid - 1] = ObjectIdGetDatum(dst_datid); ++ replaces[Anum_pg_seclabel_datid - 1] = true; ++ while (HeapTupleIsValid(oldtup = systable_getnext(scan))) ++ { ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ values, nulls, replaces); ++ simple_heap_insert(rel, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ } ++ systable_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++void ++seclabelOnDropDatabase(Oid datid) ++{ ++ Relation rel; ++ ScanKeyData keys[1]; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ ++ /* Scan all entries with pg_seclabel.datid = datid */ ++ ScanKeyInit(&keys[0], ++ Anum_pg_seclabel_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(datid)); ++ ++ rel = heap_open(SecLabelRelationId, RowExclusiveLock); ++ ++ scan = systable_beginscan(rel, SecLabelSecidIndexId, true, ++ SnapshotNow, 1, keys); ++ ++ while (HeapTupleIsValid(tuple = systable_getnext(scan))) ++ { ++ simple_heap_delete(rel, &tuple->t_self); ++ } ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++void ++seclabelOnDropTable(Oid relid) ++{ ++ Relation rel; ++ SysScanDesc scan; ++ ScanKeyData key[2]; ++ HeapTuple tuple; ++ Oid database_oid; ++ ++ database_oid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); ++ ScanKeyInit(&key[0], ++ Anum_pg_seclabel_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(database_oid)); ++ ScanKeyInit(&key[1], ++ Anum_pg_seclabel_relid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relid)); ++ ++ rel = heap_open(SecLabelRelationId, RowExclusiveLock); ++ scan = systable_beginscan(rel, SecLabelLabelIndexId, true, ++ SnapshotNow, 2, key); ++ ++ while (HeapTupleIsValid(tuple = systable_getnext(scan))) ++ simple_heap_delete(rel, &tuple->t_self); ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++/* ++ * XXX - it should be replaced by BKI scripts ++ */ ++bool ++seclabelCatalogHasSysAttr(Oid relOid) ++{ ++ switch (relOid) ++ { ++ case AggregateRelationId: ++ /* pg_aggregate is property of pg_proc */ ++ case AccessMethodOperatorRelationId: ++ /* pg_amop is property of pg_opfamily */ ++ case AccessMethodProcedureRelationId: ++ /* pg_amproc is property of pg_opfamily */ ++ case AttrDefaultRelationId: ++ /* pg_attrdef is property of pg_attribute */ ++ case AuthMemRelationId: ++ /* pg_auth_members is property of pg_auth */ ++ case ConstraintRelationId: ++ /* ++ * CHECK constraint is property of pg_class ++ * DOMAIN constraint is property of pg_type ++ * Global assertion is property of pg_database ++ */ ++ case DbRoleSettingRelationId: ++ /* pg_db_role_setting is property of pg_auth or pg_database */ ++ case DependRelationId: ++ /* property of the depending object */ ++ case DescriptionRelationId: ++ /* property of the object commented on */ ++ case EnumRelationId: ++ /* pg_enum is property of pg_type */ ++ case IndexRelationId: ++ /* pg_index is property of pg_class with RELKIND_INDEX */ ++ case InheritsRelationId: ++ /* pg_inherits is property of the child relation */ ++ case LargeObjectRelationId: ++ /* pg_largeobject is data chunk of pg_largeobject_metadata */ ++ case RewriteRelationId: ++ /* pg_rewrite is property of pg_class */ ++ case SecLabelRelationId: ++ /* No security attribute has no security label */ ++ case SharedDependRelationId: ++ /* property of the depending shared object */ ++ case SharedDescriptionRelationId: ++ /* property of the shared object commented on */ ++ case TriggerRelationId: ++ /* pg_trigger is property of pg_class */ ++ case TSConfigMapRelationId: ++ /* pg_ts_config_map is property of pg_ts_config */ ++ return false; ++ ++ default: ++ return true; ++ } ++} ++ ++Oid * ++seclabelMakeRelationDefaults(TupleDesc tupdesc, List *supOids) ++{ ++ ListCell *l; ++ Oid *secLabels; ++ Oid securityId; ++ int index, attno, nitems; ++ ++ nitems = tupdesc->natts - FirstLowInvalidHeapAttributeNumber; ++ secLabels = palloc0(sizeof(Oid) * nitems); ++ ++ foreach (l, supOids) ++ { ++ Oid relOid = lfirst_oid(l); ++ ++ securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); ++ ++ if (!OidIsValid(secLabels[0])) ++ secLabels[0] = securityId; ++ else if (!seclabelCompareSecid(RelationRelationId, secLabels[0], ++ RelationRelationId, securityId)) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("cannot inherit relations with different label"))); ++ ++ for (index = 1; index < nitems; index++) ++ { ++ attno = index + FirstLowInvalidHeapAttributeNumber; ++ ++ if (attno < 0) ++ securityId = GetSysCacheSecid2(ATTNUM, ++ ObjectIdGetDatum(relOid), ++ Int16GetDatum(attno)); ++ else ++ { ++ const char *attname = NameStr(tupdesc->attrs[attno]->attname); ++ securityId = GetSysCacheSecid2(ATTNAME, ++ ObjectIdGetDatum(relOid), ++ PointerGetDatum(attname)); ++ } ++ ++ if (!OidIsValid(securityId)) ++ continue; ++ ++ if (!OidIsValid(secLabels[index])) ++ secLabels[index] = securityId; ++ else if (!seclabelCompareSecid(AttributeRelationId, secLabels[index], ++ AttributeRelationId, securityId)) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("cannot inherit attribute with different label"))); ++ } ++ } ++ return secLabels; ++} ++ ++Oid * ++seclabelMakeToastDefaults(TupleDesc tupdesc, Oid relOid) ++{ ++ Oid *secLabels; ++ Oid securityId; ++ int index, nitems; ++ ++ nitems = tupdesc->natts + 1 - FirstLowInvalidHeapAttributeNumber; ++ secLabels = palloc0(sizeof(Oid) * nitems); ++ ++ securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); ++ secLabels[0] = securityId; ++ ++ securityId = seclabelMoveSecid(AttributeRelationId, ++ RelationRelationId, ++ securityId); ++ for (index = 1; index < nitems; index++) ++ secLabels[index] = securityId; ++ ++ return secLabels; ++} ++ ++Oid ++seclabelGetNewSecid(Relation rel, HeapTuple tuple) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled() && ++ !IsBootstrapProcessingMode()) ++ { ++ return sepgsql_get_default_secid(rel, tuple); ++ } ++#endif ++ return InvalidOid; ++} ++ ++static Oid ++inputSecurityLabel(Oid relid, const char *seclabel) ++{ ++ LOCKMODE lockmode = AccessShareLock; ++ Relation rel; ++ ScanKeyData skey[3]; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ Oid datid; ++ Oid secid; ++ Datum values[Natts_pg_seclabel]; ++ bool nulls[Natts_pg_seclabel]; ++ ++ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); ++ ++retry: ++ /* ++ * Lookup pg_seclabel first, then insert a new entry if not found. ++ * An exclusive lock is not necessary for the first read-only path, ++ * and we assume most of trials are read-only. ++ */ ++ rel = heap_open(SecLabelRelationId, lockmode); ++ ++ ScanKeyInit(&skey[0], ++ Anum_pg_seclabel_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(datid)); ++ ScanKeyInit(&skey[1], ++ Anum_pg_seclabel_relid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relid)); ++ ScanKeyInit(&skey[2], ++ Anum_pg_seclabel_label, ++ BTEqualStrategyNumber, F_TEXTEQ, ++ CStringGetTextDatum(seclabel)); ++ ++ scan = systable_beginscan(rel, SecLabelLabelIndexId, true, ++ SnapshotToast, 3, skey); ++ ++ tuple = systable_getnext(scan); ++ if (HeapTupleIsValid(tuple)) ++ { ++ secid = ((Form_pg_seclabel) GETSTRUCT(tuple))->secid; ++ systable_endscan(scan); ++ heap_close(rel, lockmode); ++ return secid; ++ } ++ ++ /* ++ * If not exist, try to insert a new entry. ++ */ ++ if (lockmode == AccessShareLock) ++ { ++ systable_endscan(scan); ++ heap_close(rel, lockmode); ++ lockmode = RowExclusiveLock; ++ goto retry; ++ } ++ ++ memset(nulls, false, sizeof(nulls)); ++ secid = GetNewOidWithIndex(rel, SecLabelSecidIndexId, ++ Anum_pg_seclabel_secid); ++ values[Anum_pg_seclabel_secid - 1] = ObjectIdGetDatum(secid); ++ values[Anum_pg_seclabel_datid - 1] = ObjectIdGetDatum(datid); ++ values[Anum_pg_seclabel_relid - 1] = ObjectIdGetDatum(relid); ++ values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(seclabel); ++ ++ tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); ++ ++ simple_heap_insert(rel, tuple); ++ ++ CatalogUpdateIndexes(rel, tuple); ++ ++ systable_endscan(scan); ++ ++ heap_close(rel, lockmode); ++ ++ return secid; ++} ++ ++static char * ++outputSecurityLabel(Oid relid, Oid secid) ++{ ++ Relation rel; ++ ScanKeyData skey[3]; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ Oid datid; ++ char *result = NULL; ++ ++ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); ++ ++ /* ++ * Lookup pg_seclabel for the given datid/relid/secid ++ */ ++ rel = heap_open(SecLabelRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey[0], ++ Anum_pg_seclabel_secid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(secid)); ++ ScanKeyInit(&skey[1], ++ Anum_pg_seclabel_datid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(datid)); ++ ScanKeyInit(&skey[2], ++ Anum_pg_seclabel_relid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relid)); ++ ++ scan = systable_beginscan(rel, SecLabelSecidIndexId, true, ++ SnapshotToast, 3, skey); ++ tuple = systable_getnext(scan); ++ if (HeapTupleIsValid(tuple)) ++ { ++ Datum datum; ++ bool isnull; ++ ++ datum = heap_getattr(tuple, ++ Anum_pg_seclabel_label, ++ RelationGetDescr(rel), &isnull); ++ if (!isnull) ++ result = TextDatumGetCString(datum); ++ } ++ systable_endscan(scan); ++ ++ heap_close(rel, AccessShareLock); ++ ++ return result; ++} ++ ++Oid ++seclabelRawInput(Oid relid, char *seclabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ seclabel = sepgsql_rawlabel_in(seclabel); ++#endif ++ return inputSecurityLabel(relid, seclabel); ++} ++ ++char * ++seclabelRawOutput(Oid relid, Oid secid) ++{ ++ char *seclabel = outputSecurityLabel(relid, secid); ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ seclabel = sepgsql_rawlabel_out(seclabel); ++#endif ++ return seclabel; ++} ++ ++Oid ++seclabelTransInput(Oid relid, char *seclabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ seclabel = sepgsql_mcstrans_in(seclabel); ++#endif ++ return seclabelRawInput(relid, seclabel); ++} ++ ++char * ++seclabelTransOutput(Oid relid, Oid secid) ++{ ++ char *seclabel = seclabelRawOutput(relid, secid); ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ seclabel = sepgsql_mcstrans_out(seclabel); ++#endif ++ return seclabel; ++} ++ ++Oid ++seclabelMoveSecid(Oid dst_relid, Oid src_relid, Oid secid) ++{ ++ char *label = seclabelRawOutput(src_relid, secid); ++ ++ if (!label) ++ return InvalidOid; ++ ++ secid = seclabelRawInput(dst_relid, label); ++ ++ pfree(label); ++ ++ return secid; ++} ++ ++bool ++seclabelCompareSecid(Oid relid1, Oid secid1, Oid relid2, Oid secid2) ++{ ++ char *label1 = seclabelRawOutput(relid1, secid1); ++ char *label2 = seclabelRawOutput(relid2, secid2); ++ bool retval = false; ++ ++ if (label1 && label2 && strcmp(label1, label2) == 0) ++ retval = true; ++ else if (!label1 && !label2) ++ retval = false; ++ ++ if (label1) ++ pfree(label1); ++ if (label2) ++ pfree(label2); ++ ++ return retval; ++} ++ ++Datum ++seclabelSysattOutput(Oid relid, HeapTuple tuple) ++{ ++ Oid secid; ++ char *seclabel; ++ ++ secid = HeapTupleGetSecid(tuple); ++ ++ seclabel = seclabelTransOutput(relid, secid); ++ if (!seclabel) ++ seclabel = "unlabeled"; ++ ++ return CStringGetTextDatum(seclabel); ++} ++ ++/* ++ * seclabelRelationReclaim ++ * ++ * It reclaims security labels already referenced to. ++ * It has to be called under the VACUUM FULL context that means the relation ++ * to be reclaimed is already locked exclusively. ++ */ ++void ++seclabelRelationReclaim(Oid relOid) ++{ ++ StringInfoData query; ++ const char *nspname_reclaimed; ++ const char *relname_reclaimed; ++ const char *nspname_pg_seclabel; ++ const char *relname_pg_seclabel; ++ const char *attname_secid; ++ const char *attname_datid; ++ const char *attname_relid; ++ const char *attname_label; ++ const char *nspname_to_secid; ++ const char *proname_to_secid; ++ Oid databaseId; ++ Oid namespaceId; ++ int index; ++ int save_sepgsql_mode; ++ ++ if (SPI_connect() != SPI_OK_CONNECT) ++ elog(ERROR, "SPI_connect() failed"); ++ ++ /* ++ * DELETE orphan entries ++ */ ++ databaseId = (IsSharedRelation(relOid) ? InvalidOid : MyDatabaseId); ++ ++ namespaceId = get_rel_namespace(relOid); ++ nspname_reclaimed = get_namespace_name(namespaceId); ++ relname_reclaimed = get_rel_name(relOid); ++ ++ namespaceId = get_rel_namespace(SecLabelRelationId); ++ nspname_pg_seclabel = get_namespace_name(namespaceId); ++ relname_pg_seclabel = get_rel_name(SecLabelRelationId); ++ ++ attname_secid = get_attname(SecLabelRelationId, Anum_pg_seclabel_secid); ++ attname_datid = get_attname(SecLabelRelationId, Anum_pg_seclabel_datid); ++ attname_relid = get_attname(SecLabelRelationId, Anum_pg_seclabel_relid); ++ attname_label = get_attname(SecLabelRelationId, Anum_pg_seclabel_label); ++ ++ namespaceId = get_func_namespace(F_SECLABEL_TO_SECID); ++ nspname_to_secid = get_namespace_name(namespaceId); ++ proname_to_secid = get_func_name(F_SECLABEL_TO_SECID); ++ ++ initStringInfo(&query); ++ appendStringInfo(&query, ++ "DELETE FROM %s.%s " ++ "WHERE %s = %u AND %s = %u AND %s NOT IN " ++ "(SELECT %s.%s(%s) FROM ONLY %s.%s) " ++ "RETURNING %s,%s", ++ quote_identifier(nspname_pg_seclabel), ++ quote_identifier(relname_pg_seclabel), ++ quote_identifier(attname_datid), ++ databaseId, ++ quote_identifier(attname_relid), ++ relOid, ++ quote_identifier(attname_secid), ++ quote_identifier(nspname_to_secid), ++ quote_identifier(proname_to_secid), ++ quote_identifier(relname_reclaimed), ++ quote_identifier(nspname_reclaimed), ++ quote_identifier(relname_reclaimed), ++ quote_identifier(attname_secid), ++ quote_identifier(attname_label)); ++ /* ++ * Run the query ++ */ ++ elog(DEBUG1, "query: %s", query.data); ++ ++ save_sepgsql_mode = sepostgresql_mode; ++ ++ PG_TRY(); ++ { ++ sepostgresql_mode = SEPGSQL_MODE_INTERNAL; ++ ++ if (SPI_execute(query.data, false, 0) != SPI_OK_DELETE_RETURNING) ++ elog(ERROR, "Failed to run: %s", query.data); ++ } ++ PG_CATCH(); ++ { ++ sepostgresql_mode = save_sepgsql_mode; ++ ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ ++ sepostgresql_mode = save_sepgsql_mode; ++ ++ for (index = 0; index < SPI_processed; index++) ++ { ++ HeapTuple tuple = SPI_tuptable->vals[index]; ++ char *label; ++ Oid secid; ++ Datum datum; ++ bool isnull; ++ ++ datum = heap_getattr(tuple, 1, SPI_tuptable->tupdesc, &isnull); ++ secid = (isnull ? InvalidOid : DatumGetObjectId(datum)); ++ ++ datum = heap_getattr(tuple, 2, SPI_tuptable->tupdesc, &isnull); ++ label = (isnull ? NULL : TextDatumGetCString(datum)); ++ ++ elog(DEBUG1, "seclabel: \"%s\"was reclaimed (secid=%u)", ++ label, secid); ++ } ++ ++ if (SPI_finish() != SPI_OK_FINISH) ++ elog(ERROR, "SPI_finish() failed"); ++} ++ ++Datum ++seclabel_to_secid(PG_FUNCTION_ARGS) ++{ ++ HeapTupleHeader htup = PG_GETARG_HEAPTUPLEHEADER(0); ++ ++ PG_RETURN_OID(HeapTupleHeaderGetSecid(htup)); ++} +diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c +index 76f9e06..0b4dcc3 100644 +--- a/src/backend/catalog/pg_type.c ++++ b/src/backend/catalog/pg_type.c +@@ -25,6 +25,7 @@ + #include "commands/typecmds.h" + #include "miscadmin.h" + #include "parser/scansup.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -57,10 +58,17 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) + Datum values[Natts_pg_type]; + bool nulls[Natts_pg_type]; + Oid typoid; ++ Oid secid; + NameData name; + + Assert(PointerIsValid(typeName)); + ++ /* SELinux checks */ ++ secid = sepgsql_type_create(typeName, InvalidOid, ++ typeNamespace, TYPTYPE_PSEUDO, ++ F_SHELL_IN, F_SHELL_OUT, ++ InvalidOid, InvalidOid, ++ InvalidOid, InvalidOid, InvalidOid); + /* + * open pg_type + */ +@@ -126,6 +134,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) + binary_upgrade_next_pg_type_oid = InvalidOid; + } + ++ HeapTupleSetSecid(tup, secid); ++ + /* + * insert the tuple in the relation and get the tuple's oid. + */ +@@ -204,7 +214,8 @@ TypeCreate(Oid newTypeOid, + char storage, + int32 typeMod, + int32 typNDims, /* Array dimensions for baseType */ +- bool typeNotNull) ++ bool typeNotNull, ++ Oid securityId) + { + Relation pg_type_desc; + Oid typeObjectId; +@@ -389,6 +400,8 @@ TypeCreate(Oid newTypeOid, + */ + if (((Form_pg_type) GETSTRUCT(tup))->typowner != ownerId) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, typeName); ++ if (HeapTupleGetSecid(tup) != securityId) ++ elog(ERROR, "Bug? security-id was mismatched"); + + /* trouble if caller wanted to force the OID */ + if (OidIsValid(newTypeOid)) +@@ -425,6 +438,8 @@ TypeCreate(Oid newTypeOid, + } + /* else allow system to assign oid */ + ++ HeapTupleSetSecid(tup, securityId); ++ + typeObjectId = simple_heap_insert(pg_type_desc, tup); + } + +diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c +index 435dfdd..5d86354 100644 +--- a/src/backend/catalog/toasting.c ++++ b/src/backend/catalog/toasting.c +@@ -24,6 +24,7 @@ + #include "catalog/namespace.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_opclass.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_type.h" + #include "catalog/toasting.h" + #include "miscadmin.h" +@@ -127,6 +128,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio + int16 coloptions[2]; + ObjectAddress baseobject, + toastobject; ++ Oid *secLabels; + + /* + * Toast table is shared if and only if its parent is. +@@ -168,7 +170,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio + "pg_toast_%u_index", relOid); + + /* this is pretty painful... need a tuple descriptor */ +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, + "chunk_id", + OIDOID, +@@ -206,6 +208,11 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio + binary_upgrade_next_pg_type_toast_oid = InvalidOid; + } + ++ /* ++ * Toast inherits security-id from the heap relation ++ */ ++ secLabels = seclabelMakeToastDefaults(tupdesc, relOid); ++ + toast_relid = heap_create_with_catalog(toast_relname, + namespaceid, + rel->rd_rel->reltablespace, +@@ -223,7 +230,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio + ONCOMMIT_NOOP, + reloptions, + false, +- true); ++ true, ++ secLabels); + + /* make the toast relation visible, else index creation will fail */ + CommandCounterIncrement(); +diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c +index 4964fb3..390a1c1 100644 +--- a/src/backend/commands/aggregatecmds.c ++++ b/src/backend/commands/aggregatecmds.c +@@ -32,6 +32,7 @@ + #include "miscadmin.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -307,6 +308,9 @@ RenameAggregate(List *name, List *args, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_proc_alter_rename(procOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname); + simple_heap_update(rel, &tup->t_self, tup); +diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c +index 17e1e77..c1c8bfd 100644 +--- a/src/backend/commands/alter.c ++++ b/src/backend/commands/alter.c +@@ -289,3 +289,64 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) + (int) stmt->objectType); + } + } ++ ++/* ++ * ExecAlterSecLabelStmt ++ * ++ * Execute ALTER xxx SECURITY LABEL TO statement ++ */ ++void ++ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt) ++{ ++ const char *name; ++ char *seclabel; ++ ++ Assert(IsA(stmt->secLabel, String)); ++ seclabel = strVal(stmt->secLabel); ++ ++ switch (stmt->objectType) ++ { ++ case OBJECT_DATABASE: ++ name = strVal(linitial(stmt->object)); ++ AlterDatabaseSecLabel(name, seclabel); ++ break; ++ ++ case OBJECT_SCHEMA: ++ name = strVal(linitial(stmt->object)); ++ AlterSchemaSecLabel(name, seclabel); ++ break; ++ ++ case OBJECT_TABLE: ++ case OBJECT_SEQUENCE: ++ case OBJECT_VIEW: ++ case OBJECT_COLUMN: ++ AlterRelationSecLabel(stmt->relation, stmt->addname, ++ stmt->objectType, seclabel); ++ break; ++ ++ case OBJECT_AGGREGATE: ++ AlterFunctionSecLabel(stmt->object, stmt->objarg, true, seclabel); ++ break; ++ ++ case OBJECT_FUNCTION: ++ AlterFunctionSecLabel(stmt->object, stmt->objarg, false, seclabel); ++ break; ++ ++ case OBJECT_LARGEOBJECT: ++ LargeObjectAlterSecLabel(intVal(linitial(stmt->object)), seclabel); ++ break; ++ ++ case OBJECT_TYPE: ++ case OBJECT_DOMAIN: ++ AlterTypeSecLabel(stmt->object, seclabel); ++ break; ++ ++ case OBJECT_TABLESPACE: ++ AlterTableSpaceSecLabel(strVal(linitial(stmt->object)), seclabel); ++ break; ++ ++ default: ++ elog(ERROR, "unrecognized AlterSecLabelStmt type: %d", ++ (int) stmt->objectType); ++ } ++} +diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c +index ccb4599..394740a 100644 +--- a/src/backend/commands/cluster.c ++++ b/src/backend/commands/cluster.c +@@ -30,12 +30,14 @@ + #include "catalog/indexing.h" + #include "catalog/namespace.h" + #include "catalog/pg_namespace.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/toasting.h" + #include "commands/cluster.h" + #include "commands/tablecmds.h" + #include "commands/trigger.h" + #include "commands/vacuum.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/procarray.h" + #include "storage/smgr.h" +@@ -116,6 +118,9 @@ cluster(ClusterStmt *stmt, bool isTopLevel) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + ++ /* SELinux checks */ ++ sepgsql_relation_cluster(tableOid, true); ++ + /* + * Reject clustering a remote temp table ... their local buffer + * manager is not going to cope. +@@ -290,7 +295,8 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose, + Form_pg_index indexForm; + + /* Check that the user still owns the relation */ +- if (!pg_class_ownercheck(tableOid, GetUserId())) ++ if (!pg_class_ownercheck(tableOid, GetUserId()) || ++ !sepgsql_relation_cluster(tableOid, false)) + { + relation_close(OldHeap, AccessExclusiveLock); + return; +@@ -632,6 +638,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) + HeapTuple tuple; + Datum reloptions; + bool isNull; ++ Oid *secLabels; + + OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock); + OldHeapDesc = RelationGetDescr(OldHeap); +@@ -657,6 +664,11 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) + reloptions = (Datum) 0; + + /* ++ * The new heap copies all the security-id from the original ++ */ ++ secLabels = seclabelMakeRelationDefaults(tupdesc, ++ list_make1_oid(OIDOldHeap)); ++ /* + * Create the new heap, using a temporary name in the same namespace as + * the existing table. NOTE: there is some risk of collision with user + * relnames. Working around this seems more trouble than it's worth; in +@@ -687,7 +699,8 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) + ONCOMMIT_NOOP, + reloptions, + false, +- true); ++ true, ++ secLabels); + + ReleaseSysCache(tuple); + +@@ -977,6 +990,9 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, + /* Preserve OID, if any */ + if (NewHeap->rd_rel->relhasoids) + HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); ++ /* Preserve security-id, if any */ ++ if (NewHeap->rd_rel->relhassecids) ++ HeapTupleSetSecid(copiedTuple, HeapTupleGetSecid(tuple)); + + /* The heap rewrite module does the rest */ + rewrite_heap_tuple(rwstate, tuple, copiedTuple); +@@ -1464,7 +1480,8 @@ get_tables_to_cluster(MemoryContext cluster_context) + { + index = (Form_pg_index) GETSTRUCT(indexTuple); + +- if (!pg_class_ownercheck(index->indrelid, GetUserId())) ++ if (!pg_class_ownercheck(index->indrelid, GetUserId()) || ++ !sepgsql_relation_cluster(index->indrelid, false)) + continue; + + /* +diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c +index 64792f2..6d71642 100644 +--- a/src/backend/commands/comment.c ++++ b/src/backend/commands/comment.c +@@ -49,6 +49,7 @@ + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -548,6 +549,9 @@ CommentRelation(int objtype, List *relname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(relation)); + ++ /* SELinux checks */ ++ sepgsql_relation_comment(RelationGetRelid(relation)); ++ + /* Next, verify that the relation type matches the intent */ + + switch (objtype) +@@ -651,6 +655,9 @@ CommentAttribute(List *qualname, char *comment) + errmsg("column \"%s\" of relation \"%s\" does not exist", + attrname, RelationGetRelationName(relation)))); + ++ /* SELinux checks */ ++ sepgsql_attribute_comment(RelationGetRelid(relation), attnum); ++ + /* Create the comment using the relation's oid */ + CreateComments(RelationGetRelid(relation), RelationRelationId, + (int32) attnum, comment); +@@ -704,6 +711,9 @@ CommentDatabase(List *qualname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + database); + ++ /* SELinux checks */ ++ sepgsql_database_comment(oid); ++ + /* Call CreateSharedComments() to create/drop the comments */ + CreateSharedComments(oid, DatabaseRelationId, comment); + } +@@ -742,6 +752,9 @@ CommentTablespace(List *qualname, char *comment) + if (!pg_tablespace_ownercheck(oid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, tablespace); + ++ /* SELinux checks */ ++ sepgsql_tablespace_comment(oid); ++ + /* Call CreateSharedComments() to create/drop the comments */ + CreateSharedComments(oid, TableSpaceRelationId, comment); + } +@@ -774,6 +787,9 @@ CommentRole(List *qualname, char *comment) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be member of role \"%s\" to comment upon it", role))); + ++ /* SELinux checks */ ++ sepgsql_role_comment(oid); ++ + /* Call CreateSharedComments() to create/drop the comments */ + CreateSharedComments(oid, AuthIdRelationId, comment); + } +@@ -810,6 +826,9 @@ CommentNamespace(List *qualname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, + namespace); + ++ /* SELinux checks */ ++ sepgsql_schema_comment(oid); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(oid, NamespaceRelationId, 0, comment); + } +@@ -919,6 +938,9 @@ CommentRule(List *qualname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(reloid)); + ++ /* SELinux checks */ ++ sepgsql_rule_comment(reloid, rulename); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(ruleoid, RewriteRelationId, 0, comment); + +@@ -953,6 +975,9 @@ CommentType(List *typename, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, + format_type_be(oid)); + ++ /* SELinux checks */ ++ sepgsql_type_comment(oid); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(oid, TypeRelationId, 0, comment); + } +@@ -977,6 +1002,9 @@ CommentAggregate(List *aggregate, List *arguments, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(aggregate)); + ++ /* SELinux checks */ ++ sepgsql_proc_comment(oid); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(oid, ProcedureRelationId, 0, comment); + } +@@ -1005,6 +1033,9 @@ CommentProc(List *function, List *arguments, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(function)); + ++ /* SELinux checks */ ++ sepgsql_proc_comment(oid); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(oid, ProcedureRelationId, 0, comment); + } +@@ -1036,6 +1067,9 @@ CommentOperator(List *opername, List *arguments, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, + NameListToString(opername)); + ++ /* SELinux checks */ ++ sepgsql_operator_comment(oid); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(oid, OperatorRelationId, 0, comment); + } +@@ -1080,6 +1114,9 @@ CommentTrigger(List *qualname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(relation)); + ++ /* SELinux checks */ ++ sepgsql_trigger_comment(RelationGetRelid(relation), trigname); ++ + /* + * Fetch the trigger tuple from pg_trigger. There can be only one because + * of the unique index. +@@ -1153,6 +1190,9 @@ CommentConstraint(List *qualname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(relation)); + ++ /* SELinux checks */ ++ sepgsql_constraint_comment(RelationGetRelid(relation), conName); ++ + conOid = GetConstraintByName(RelationGetRelid(relation), conName); + + /* Call CreateComments() to create/drop the comments */ +@@ -1188,6 +1228,9 @@ CommentConversion(List *qualname, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, + NameListToString(qualname)); + ++ /* SELinux checks */ ++ sepgsql_conversion_comment(conversionOid); ++ + /* Call CreateComments() to create/drop the comments */ + CreateComments(conversionOid, ConversionRelationId, 0, comment); + } +@@ -1304,6 +1347,9 @@ CommentOpClass(List *qualname, List *arguments, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, + NameListToString(qualname)); + ++ /* SELinux checks */ ++ sepgsql_opclass_comment(opcID); ++ + ReleaseSysCache(tuple); + + /* Call CreateComments() to create/drop the comments */ +@@ -1385,6 +1431,9 @@ CommentOpFamily(List *qualname, List *arguments, char *comment) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, + NameListToString(qualname)); + ++ /* SELinux checks */ ++ sepgsql_opfamily_comment(opfID); ++ + ReleaseSysCache(tuple); + + /* Call CreateComments() to create/drop the comments */ +@@ -1505,6 +1554,9 @@ CommentCast(List *qualname, List *arguments, char *comment) + format_type_be(sourcetypeid), + format_type_be(targettypeid)))); + ++ /* SELinux checks */ ++ sepgsql_cast_comment(sourcetypeid, targettypeid); ++ + ReleaseSysCache(tuple); + + /* Call CreateComments() to create/drop the comments */ +@@ -1522,6 +1574,8 @@ CommentTSParser(List *qualname, char *comment) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to comment on text search parser"))); ++ /* SELinux checks */ ++ sepgsql_ts_parser_comment(prsId); + + CreateComments(prsId, TSParserRelationId, 0, comment); + } +@@ -1536,6 +1590,8 @@ CommentTSDictionary(List *qualname, char *comment) + if (!pg_ts_dict_ownercheck(dictId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, + NameListToString(qualname)); ++ /* SELinux checks */ ++ sepgsql_ts_dict_comment(dictId); + + CreateComments(dictId, TSDictionaryRelationId, 0, comment); + } +@@ -1551,6 +1607,8 @@ CommentTSTemplate(List *qualname, char *comment) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to comment on text search template"))); ++ /* SELinux checks */ ++ sepgsql_ts_template_comment(tmplId); + + CreateComments(tmplId, TSTemplateRelationId, 0, comment); + } +@@ -1565,6 +1623,8 @@ CommentTSConfiguration(List *qualname, char *comment) + if (!pg_ts_config_ownercheck(cfgId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, + NameListToString(qualname)); ++ /* SELinux checks */ ++ sepgsql_ts_config_comment(cfgId); + + CreateComments(cfgId, TSConfigRelationId, 0, comment); + } +diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c +index 57ddab0..0c10a64 100644 +--- a/src/backend/commands/conversioncmds.c ++++ b/src/backend/commands/conversioncmds.c +@@ -24,6 +24,7 @@ + #include "mb/pg_wchar.h" + #include "miscadmin.h" + #include "parser/parse_func.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -45,6 +46,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) + int from_encoding; + int to_encoding; + Oid funcoid; ++ Oid secid; + const char *from_encoding_name = stmt->for_encoding_name; + const char *to_encoding_name = stmt->to_encoding_name; + List *func_name = stmt->func_name; +@@ -96,6 +98,10 @@ CreateConversionCommand(CreateConversionStmt *stmt) + aclcheck_error(aclresult, ACL_KIND_PROC, + NameListToString(func_name)); + ++ /* SELinux checks */ ++ secid = sepgsql_conversion_create(conversion_name, ++ namespaceId, funcoid); ++ + /* + * Check that the conversion function is suitable for the requested source + * and target encodings. We do that by calling the function with an empty +@@ -114,7 +120,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) + * name) + */ + ConversionCreate(conversion_name, namespaceId, GetUserId(), +- from_encoding, to_encoding, funcoid, stmt->def); ++ from_encoding, to_encoding, funcoid, stmt->def, secid); + } + + /* +@@ -174,6 +180,9 @@ DropConversionsCommand(DropStmt *drop) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, + NameStr(con->conname)); + ++ /* SELinux checks */ ++ sepgsql_conversion_drop(conversionOid, false); ++ + object.classId = ConversionRelationId; + object.objectId = conversionOid; + object.objectSubId = 0; +@@ -235,6 +244,9 @@ RenameConversion(List *name, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_conversion_alter_rename(conversionOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -329,6 +341,8 @@ AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(convForm->connamespace)); + } ++ /* SELinux checks */ ++ sepgsql_conversion_alter(HeapTupleGetOid(tup)); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c +index 9d46e47..270eb74 100644 +--- a/src/backend/commands/copy.c ++++ b/src/backend/commands/copy.c +@@ -22,7 +22,10 @@ + + #include "access/heapam.h" + #include "access/xact.h" ++#include "access/sysattr.h" ++#include "catalog/heap.h" + #include "catalog/namespace.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_type.h" + #include "commands/copy.h" + #include "commands/defrem.h" +@@ -35,6 +38,7 @@ + #include "optimizer/planner.h" + #include "parser/parse_relation.h" + #include "rewrite/rewriteHandler.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" +@@ -161,6 +165,12 @@ typedef struct CopyStateData + char *raw_buf; + int raw_buf_index; /* next byte to process */ + int raw_buf_len; /* total # of bytes stored */ ++ ++ /* ++ * Dump/Restore support for security_label ++ */ ++ FmgrInfo seclabel_out_function; ++ bool seclabel_force_quot; + } CopyStateData; + + typedef CopyStateData *CopyState; +@@ -244,7 +254,7 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0"; + /* non-export function prototypes */ + static void DoCopyTo(CopyState cstate); + static void CopyTo(CopyState cstate); +-static void CopyOneRowTo(CopyState cstate, Oid tupleOid, ++static void CopyOneRowTo(CopyState cstate, HeapTuple tuple, + Datum *values, bool *nulls); + static void CopyFrom(CopyState cstate); + static bool CopyReadLine(CopyState cstate); +@@ -986,8 +996,21 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + errhint("Anyone can COPY to stdout or from stdin. " + "psql's \\copy command also works for anyone."))); + ++ /* SELinux checks on file COPY */ ++ if (stmt->filename) ++ { ++ if (is_from) ++ sepgsql_file_read(stmt->filename); ++ else ++ sepgsql_file_write(stmt->filename, true); ++ } ++ + if (stmt->relation) + { ++ Bitmapset *columnsSet = NULL; ++ List *attnums; ++ ListCell *cur; ++ + Assert(!stmt->query); + cstate->queryDesc = NULL; + +@@ -998,16 +1021,20 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + tupDesc = RelationGetDescr(cstate->rel); + + /* Check relation permissions. */ ++ attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); ++ foreach(cur, attnums) ++ { ++ int index = lfirst_int(cur) ++ - FirstLowInvalidHeapAttributeNumber; ++ columnsSet = bms_add_member(columnsSet, index); ++ } ++ + relPerms = pg_class_aclmask(RelationGetRelid(cstate->rel), GetUserId(), + required_access, ACLMASK_ALL); + remainingPerms = required_access & ~relPerms; + if (remainingPerms != 0) + { + /* We don't have table permissions, check per-column permissions */ +- List *attnums; +- ListCell *cur; +- +- attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); + foreach(cur, attnums) + { + int attnum = lfirst_int(cur); +@@ -1021,6 +1048,13 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + } + } + ++ if (is_from) ++ sepgsql_relation_perms(RelationGetRelid(cstate->rel), ++ ACL_INSERT, NULL, columnsSet, true); ++ else ++ sepgsql_relation_perms(RelationGetRelid(cstate->rel), ++ ACL_SELECT, columnsSet, NULL, true); ++ + /* check read-only transaction */ + if (XactReadOnly && is_from && !cstate->rel->rd_islocaltemp) + PreventCommandIfReadOnly("COPY FROM"); +@@ -1130,11 +1164,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + int attnum = lfirst_int(cur); + + if (!list_member_int(cstate->attnumlist, attnum)) ++ { ++ Form_pg_attribute attForm; ++ ++ if (attnum > 0) ++ attForm = tupDesc->attrs[attnum - 1]; ++ else ++ attForm = SystemAttributeDefinition(attnum, true, true); ++ + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", +- NameStr(tupDesc->attrs[attnum - 1]->attname)))); +- cstate->force_quote_flags[attnum - 1] = true; ++ NameStr(attForm->attname)))); ++ } ++ ++ if (attnum == SecurityLabelAttributeNumber) ++ cstate->seclabel_force_quot = true; ++ else ++ cstate->force_quote_flags[attnum - 1] = true; + } + } + +@@ -1152,10 +1199,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString) + int attnum = lfirst_int(cur); + + if (!list_member_int(cstate->attnumlist, attnum)) ++ { ++ Form_pg_attribute attForm; ++ ++ if (attnum > 0) ++ attForm = tupDesc->attrs[attnum - 1]; ++ else ++ attForm = SystemAttributeDefinition(attnum, ++ tupDesc->tdhasoid, ++ tupDesc->tdhassecid); ++ + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", +- NameStr(tupDesc->attrs[attnum - 1]->attname)))); ++ NameStr(attForm->attname)))); ++ } ++ /* ignore system columns, if specified */ ++ if (attnum <= 0) ++ continue; + cstate->force_notnull_flags[attnum - 1] = true; + } + } +@@ -1347,16 +1408,29 @@ CopyTo(CopyState cstate) + int attnum = lfirst_int(cur); + Oid out_func_oid; + bool isvarlena; ++ FmgrInfo *out_fmgr; ++ Form_pg_attribute attForm; ++ ++ if (attnum == SecurityLabelAttributeNumber) ++ { ++ attForm = SystemAttributeDefinition(attnum, true, true); ++ out_fmgr = &cstate->seclabel_out_function; ++ } ++ else ++ { ++ attForm = attr[attnum - 1]; ++ out_fmgr = &cstate->out_functions[attnum - 1]; ++ } + + if (cstate->binary) +- getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, ++ getTypeBinaryOutputInfo(attForm->atttypid, + &out_func_oid, + &isvarlena); + else +- getTypeOutputInfo(attr[attnum - 1]->atttypid, ++ getTypeOutputInfo(attForm->atttypid, + &out_func_oid, + &isvarlena); +- fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); ++ fmgr_info(out_func_oid, out_fmgr); + } + + /* +@@ -1411,7 +1485,17 @@ CopyTo(CopyState cstate) + CopySendChar(cstate, cstate->delim[0]); + hdr_delim = true; + +- colname = NameStr(attr[attnum - 1]->attname); ++ if (SystemAttributeWritable(attnum, ++ tupDesc->tdhasoid, ++ tupDesc->tdhassecid)) ++ { ++ Form_pg_attribute attForm ++ = SystemAttributeDefinition(attnum, true, true); ++ ++ colname = NameStr(attForm->attname); ++ } ++ else ++ colname = NameStr(attr[attnum - 1]->attname); + + CopyAttributeOutCSV(cstate, colname, false, + list_length(cstate->attnumlist) == 1); +@@ -1437,11 +1521,15 @@ CopyTo(CopyState cstate) + { + CHECK_FOR_INTERRUPTS(); + ++ /* Row-level access control policy */ ++ if (!sepgsql_rowlv_copyto(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); +@@ -1467,13 +1555,17 @@ CopyTo(CopyState cstate) + * Emit one row during CopyTo(). + */ + static void +-CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) ++CopyOneRowTo(CopyState cstate, HeapTuple tuple, Datum *values, bool *nulls) + { + bool need_delim = false; + FmgrInfo *out_functions = cstate->out_functions; + MemoryContext oldcontext; + ListCell *cur; + char *string; ++ Oid tupleOid = InvalidOid; ++ ++ if (HeapTupleIsValid(tuple)) ++ tupleOid = HeapTupleGetOid(tuple); + + MemoryContextReset(cstate->rowcontext); + oldcontext = MemoryContextSwitchTo(cstate->rowcontext); +@@ -1506,8 +1598,10 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); +- Datum value = values[attnum - 1]; +- bool isnull = nulls[attnum - 1]; ++ Datum value; ++ bool isnull; ++ bool force_quot; ++ FmgrInfo *out_fmgr; + + if (!cstate->binary) + { +@@ -1516,6 +1610,21 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + need_delim = true; + } + ++ if (attnum == SecurityLabelAttributeNumber) ++ { ++ value = seclabelSysattOutput(RelationGetRelid(cstate->rel), tuple); ++ isnull = false; ++ force_quot = cstate->seclabel_force_quot; ++ out_fmgr = &cstate->seclabel_out_function; ++ } ++ else ++ { ++ value = values[attnum - 1]; ++ isnull = nulls[attnum - 1]; ++ force_quot = cstate->force_quote_flags[attnum - 1]; ++ out_fmgr = &out_functions[attnum - 1]; ++ } ++ + if (isnull) + { + if (!cstate->binary) +@@ -1527,11 +1636,9 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + { + if (!cstate->binary) + { +- string = OutputFunctionCall(&out_functions[attnum - 1], +- value); ++ string = OutputFunctionCall(out_fmgr, value); + if (cstate->csv_mode) +- CopyAttributeOutCSV(cstate, string, +- cstate->force_quote_flags[attnum - 1], ++ CopyAttributeOutCSV(cstate, string, force_quot, + list_length(cstate->attnumlist) == 1); + else + CopyAttributeOutText(cstate, string); +@@ -1540,8 +1647,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + { + bytea *outputbytes; + +- outputbytes = SendFunctionCall(&out_functions[attnum - 1], +- value); ++ outputbytes = SendFunctionCall(out_fmgr, value); + CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); + CopySendData(cstate, VARDATA(outputbytes), + VARSIZE(outputbytes) - VARHDRSZ); +@@ -1675,8 +1781,10 @@ CopyFrom(CopyState cstate) + num_defaults; + FmgrInfo *in_functions; + FmgrInfo oid_in_function; ++ FmgrInfo seclabel_in_function; + Oid *typioparams; + Oid oid_typioparam; ++ Oid seclabel_typioparam; + int attnum; + int i; + Oid in_func_oid; +@@ -1919,6 +2027,19 @@ CopyFrom(CopyState cstate) + fmgr_info(in_func_oid, &oid_in_function); + } + ++ if (list_member_int(cstate->attnumlist, SecurityLabelAttributeNumber)) ++ { ++ if (!cstate->binary) ++ getTypeInputInfo(TEXTOID, &in_func_oid, &seclabel_typioparam); ++ else ++ getTypeBinaryInputInfo(TEXTOID, &in_func_oid, &seclabel_typioparam); ++ ++ fmgr_info(in_func_oid, &seclabel_in_function); ++ } ++ ++ ++ ++ + values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); + nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); + +@@ -1953,6 +2074,7 @@ CopyFrom(CopyState cstate) + { + bool skip_tuple; + Oid loaded_oid = InvalidOid; ++ Oid loaded_secid = InvalidOid; + + CHECK_FOR_INTERRUPTS(); + +@@ -2024,14 +2146,20 @@ CopyFrom(CopyState cstate) + /* Loop to read the user attributes on the line. */ + foreach(cur, cstate->attnumlist) + { ++ Form_pg_attribute attForm; + int attnum = lfirst_int(cur); + int m = attnum - 1; + ++ if (attnum == SecurityLabelAttributeNumber) ++ attForm = SystemAttributeDefinition(attnum, true, true); ++ else ++ attForm = attr[m]; ++ + if (fieldno >= fldct) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("missing data for column \"%s\"", +- NameStr(attr[m]->attname)))); ++ NameStr(attForm->attname)))); + string = field_strings[fieldno++]; + + if (cstate->csv_mode && string == NULL && +@@ -2041,14 +2169,38 @@ CopyFrom(CopyState cstate) + string = cstate->null_print; + } + +- cstate->cur_attname = NameStr(attr[m]->attname); ++ cstate->cur_attname = NameStr(attForm->attname); + cstate->cur_attval = string; +- values[m] = InputFunctionCall(&in_functions[m], +- string, +- typioparams[m], +- attr[m]->atttypmod); +- if (string != NULL) +- nulls[m] = false; ++ ++ if (attnum == SecurityLabelAttributeNumber) ++ { ++ if (string && !ignore_security_label_input) ++ { ++ Datum datum = InputFunctionCall(&seclabel_in_function, ++ string, ++ seclabel_typioparam, ++ attForm->atttypmod); ++ loaded_secid ++ = seclabelTransInput(RelationGetRelid(cstate->rel), ++ TextDatumGetCString(datum)); ++ } ++ } ++ else ++ { ++ if (cstate->csv_mode && string == NULL && ++ cstate->force_notnull_flags[m]) ++ { ++ /* Go ahead and read the NULL string */ ++ string = cstate->null_print; ++ } ++ ++ values[m] = InputFunctionCall(&in_functions[m], ++ string, ++ typioparams[m], ++ attForm->atttypmod); ++ if (string != NULL) ++ nulls[m] = false; ++ } + cstate->cur_attname = NULL; + cstate->cur_attval = NULL; + } +@@ -2094,17 +2246,38 @@ CopyFrom(CopyState cstate) + i = 0; + foreach(cur, cstate->attnumlist) + { ++ Form_pg_attribute attForm; + int attnum = lfirst_int(cur); + int m = attnum - 1; + +- cstate->cur_attname = NameStr(attr[m]->attname); ++ if (attnum == SecurityLabelAttributeNumber) ++ attForm = SystemAttributeDefinition(attnum, true, true); ++ else ++ attForm = attr[m]; ++ ++ cstate->cur_attname = NameStr(attForm->attname); + i++; +- values[m] = CopyReadBinaryAttribute(cstate, +- i, +- &in_functions[m], +- typioparams[m], +- attr[m]->atttypmod, +- &nulls[m]); ++ ++ if (attnum == SecurityLabelAttributeNumber) ++ { ++ Datum datum = CopyReadBinaryAttribute(cstate, i, ++ &seclabel_in_function, ++ seclabel_typioparam, ++ attForm->atttypmod, ++ &isnull); ++ if (!isnull && !ignore_security_label_input) ++ loaded_secid ++ = seclabelTransInput(RelationGetRelid(cstate->rel), ++ TextDatumGetCString(datum)); ++ } ++ else ++ { ++ values[m] = CopyReadBinaryAttribute(cstate, i, ++ &in_functions[m], ++ typioparams[m], ++ attr[m]->atttypmod, ++ &nulls[m]); ++ } + cstate->cur_attname = NULL; + } + } +@@ -2125,6 +2298,8 @@ CopyFrom(CopyState cstate) + + if (cstate->oids && file_has_oids) + HeapTupleSetOid(tuple, loaded_oid); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, loaded_secid); + + /* Triggers and stuff need to be invoked in query context. */ + MemoryContextSwitchTo(oldcontext); +@@ -2149,6 +2324,9 @@ CopyFrom(CopyState cstate) + } + + if (!skip_tuple) ++ sepgsql_tuple_insert(cstate->rel, tuple); ++ ++ if (!skip_tuple) + { + List *recheckIndexes = NIL; + +@@ -3435,6 +3613,17 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) + } + if (attnum == InvalidAttrNumber) + { ++ Form_pg_attribute attForm; ++ bool hasoid = tupDesc->tdhasoid; ++ bool hassecid = tupDesc->tdhassecid; ++ ++ attForm = SystemAttributeByName(name, hasoid, hassecid); ++ if (attForm && ++ SystemAttributeWritable(attForm->attnum, hasoid, hassecid)) ++ attnum = attForm->attnum; ++ } ++ if (attnum == InvalidAttrNumber) ++ { + if (rel != NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), +@@ -3482,7 +3671,7 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self) + slot_getallattrs(slot); + + /* And send the data */ +- CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); ++ CopyOneRowTo(cstate, slot->tts_tuple, slot->tts_values, slot->tts_isnull); + } + + /* +diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c +index e7dac22..16360db 100644 +--- a/src/backend/commands/dbcommands.c ++++ b/src/backend/commands/dbcommands.c +@@ -35,6 +35,8 @@ + #include "catalog/pg_authid.h" + #include "catalog/pg_database.h" + #include "catalog/pg_db_role_setting.h" ++#include "catalog/pg_seclabel.h" ++#include "catalog/pg_shdescription.h" + #include "catalog/pg_tablespace.h" + #include "commands/comment.h" + #include "commands/dbcommands.h" +@@ -43,6 +45,7 @@ + #include "miscadmin.h" + #include "pgstat.h" + #include "postmaster/bgwriter.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "storage/lmgr.h" +@@ -111,6 +114,7 @@ createdb(const CreatedbStmt *stmt) + Datum new_record[Natts_pg_database]; + bool new_record_nulls[Natts_pg_database]; + Oid dboid; ++ Oid dbsecid; + Oid datdba; + ListCell *option; + DefElem *dtablespacename = NULL; +@@ -486,6 +490,9 @@ createdb(const CreatedbStmt *stmt) + /* Note there is no additional permission check in this path */ + } + ++ /* SELinux permission checks */ ++ dbsecid = sepgsql_database_create(dbname, src_dboid); ++ + /* + * Check for db name conflict. This is just to give a more friendly error + * message than "unique index violation". There's a race condition but +@@ -560,6 +567,8 @@ createdb(const CreatedbStmt *stmt) + new_record, new_record_nulls); + + HeapTupleSetOid(tuple, dboid); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, dbsecid); + + simple_heap_insert(pg_database_rel, tuple); + +@@ -576,6 +585,9 @@ createdb(const CreatedbStmt *stmt) + /* Create pg_shdepend entries for objects within database */ + copyTemplateDependencies(src_dboid, dboid); + ++ /* Create pg_seclabel entries for objects within database */ ++ seclabelOnCreateDatabase(src_dboid, dboid); ++ + /* + * Force a checkpoint before starting the copy. This will force dirty + * buffers out to disk, to ensure source database is up-to-date on disk +@@ -777,6 +789,9 @@ dropdb(const char *dbname, bool missing_ok) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + dbname); + ++ /* SELinux checks */ ++ sepgsql_database_drop(db_id, false); ++ + /* + * Disallow dropping a DB that is marked istemplate. This is just to + * prevent people from accidentally dropping template0 or template1; they +@@ -833,6 +848,11 @@ dropdb(const char *dbname, bool missing_ok) + dropDatabaseDependencies(db_id); + + /* ++ * Remove pg_seclabel entries for the database ++ */ ++ seclabelOnDropDatabase(db_id); ++ ++ /* + * Drop pages for this database that are in the shared buffer cache. This + * is important to ensure that no remaining backend tries to write out a + * dirty buffer to the dead database later... +@@ -915,6 +935,9 @@ RenameDatabase(const char *oldname, const char *newname) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to rename database"))); + ++ /* SELinux checks */ ++ sepgsql_database_alter(db_id); ++ + /* + * Make sure the new name doesn't exist. See notes for same error in + * CREATE DATABASE. +@@ -1053,6 +1076,9 @@ movedb(const char *dbname, const char *tblspcname) + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("pg_global cannot be used as default tablespace"))); + ++ /* SELinux checks */ ++ sepgsql_database_alter(db_id); ++ + /* + * No-op if same tablespace + */ +@@ -1369,6 +1395,9 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + stmt->dbname); + ++ /* SELinux checks */ ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); ++ + /* + * Build an updated tuple, perusing the information just obtained + */ +@@ -1419,6 +1448,9 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + stmt->dbname); + ++ /* SELinux checks */ ++ sepgsql_database_alter(datid); ++ + AlterSetting(datid, InvalidOid, stmt->setstmt); + + UnlockSharedObject(DatabaseRelationId, datid, 0, AccessShareLock); +@@ -1494,6 +1526,9 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to change owner of database"))); + ++ /* SELinux checks */ ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); ++ + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + +@@ -1533,6 +1568,58 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) + heap_close(rel, NoLock); + } + ++/* ++ * ALTER DATABASE SECURITY LABEL TO ++ */ ++void ++AlterDatabaseSecLabel(const char *dbname, char *new_label) ++{ ++ Relation rel; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ Oid databaseId; ++ Oid securityId; ++ ++ /* Fetch the old tuple */ ++ rel = heap_open(DatabaseRelationId, RowExclusiveLock); ++ ScanKeyInit(&skey, ++ Anum_pg_database_datname, ++ BTEqualStrategyNumber, F_NAMEEQ, ++ NameGetDatum(dbname)); ++ scan = systable_beginscan(rel, DatabaseNameIndexId, true, ++ SnapshotNow, 1, &skey); ++ oldtup = systable_getnext(scan); ++ if (!HeapTupleIsValid(oldtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_DATABASE), ++ errmsg("database \"%s\" does not exist", dbname))); ++ ++ newtup = heap_copytuple(oldtup); ++ ++ systable_endscan(scan); ++ ++ databaseId = HeapTupleGetOid(newtup); ++ ++ /* DAC permission checks */ ++ if (!pg_database_ownercheck(databaseId, GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); ++ ++ /* SELinux checks */ ++ securityId = sepgsql_database_relabel(databaseId, new_label); ++ ++ /* Update it */ ++ HeapTupleSetSecid(newtup, securityId); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ ++ heap_close(rel, RowExclusiveLock); ++} + + /* + * Helper functions +diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c +index 0bda24a..334b123 100644 +--- a/src/backend/commands/explain.c ++++ b/src/backend/commands/explain.c +@@ -257,7 +257,7 @@ ExplainResultDesc(ExplainStmt *stmt) + } + + /* Need a tuple descriptor representing a single TEXT or XML column */ +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN", + xml ? XMLOID : TEXTOID, -1, 0); + return tupdesc; +diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c +index 14356a2..d1f255e 100644 +--- a/src/backend/commands/foreigncmds.c ++++ b/src/backend/commands/foreigncmds.c +@@ -27,6 +27,7 @@ + #include "foreign/foreign.h" + #include "miscadmin.h" + #include "parser/parse_func.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -230,6 +231,9 @@ AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId) + fdwId = HeapTupleGetOid(tup); + form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup); + ++ /* SELinux checks */ ++ sepgsql_fdw_alter(fdwId, InvalidOid); ++ + if (form->fdwowner != newOwnerId) + { + form->fdwowner = newOwnerId; +@@ -294,6 +298,8 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + } + } ++ /* SELinux checks */ ++ sepgsql_fserver_alter(srvId); + + form->srvowner = newOwnerId; + +@@ -339,6 +345,7 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) + Oid fdwvalidator; + Datum fdwoptions; + Oid ownerId; ++ Oid securityId; + + /* Must be super user */ + if (!superuser()) +@@ -391,8 +398,13 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) + else + nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; + ++ /* SELinux checks */ ++ securityId = sepgsql_fdw_create(stmt->fdwname, fdwvalidator); ++ + tuple = heap_form_tuple(rel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tuple, securityId); ++ + fdwId = simple_heap_insert(rel, tuple); + CatalogUpdateIndexes(rel, tuple); + +@@ -511,6 +523,8 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt) + + repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; + } ++ /* SELinux checks */ ++ sepgsql_fdw_alter(fdwId, fdwvalidator); + + /* Everything looks good - update the tuple */ + +@@ -559,6 +573,8 @@ RemoveForeignDataWrapper(DropFdwStmt *stmt) + stmt->fdwname))); + return; + } ++ /* SELinux checks */ ++ sepgsql_fdw_drop(fdwId, false); + + /* + * Do the deletion +@@ -608,6 +624,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt) + HeapTuple tuple; + Oid srvId; + Oid ownerId; ++ Oid securityId; + AclResult aclresult; + ObjectAddress myself; + ObjectAddress referenced; +@@ -635,6 +652,9 @@ CreateForeignServer(CreateForeignServerStmt *stmt) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + ++ /* SELinux checks */ ++ securityId = sepgsql_fserver_create(stmt->servername, fdw->fdwid); ++ + /* + * Insert tuple into pg_foreign_server. + */ +@@ -678,6 +698,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt) + + tuple = heap_form_tuple(rel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tuple, securityId); ++ + srvId = simple_heap_insert(rel, tuple); + + CatalogUpdateIndexes(rel, tuple); +@@ -732,6 +754,9 @@ AlterForeignServer(AlterForeignServerStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, + stmt->servername); + ++ /* SELinux checks */ ++ sepgsql_fserver_alter(srvId); ++ + memset(repl_val, 0, sizeof(repl_val)); + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); +@@ -823,6 +848,8 @@ RemoveForeignServer(DropForeignServerStmt *stmt) + if (!pg_foreign_server_ownercheck(srvId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, + stmt->servername); ++ /* SELinux checks */ ++ sepgsql_fserver_drop(srvId, false); + + object.classId = ForeignServerRelationId; + object.objectId = srvId; +@@ -896,6 +923,7 @@ CreateUserMapping(CreateUserMappingStmt *stmt) + HeapTuple tuple; + Oid useId; + Oid umId; ++ Oid securityId; + ObjectAddress myself; + ObjectAddress referenced; + ForeignServer *srv; +@@ -908,6 +936,9 @@ CreateUserMapping(CreateUserMappingStmt *stmt) + + user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername); + ++ /* SELinux checks */ ++ securityId = sepgsql_user_mapping_create(useId, srv->serverid); ++ + /* + * Check that the user mapping is unique within server. + */ +@@ -947,6 +978,8 @@ CreateUserMapping(CreateUserMappingStmt *stmt) + + tuple = heap_form_tuple(rel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tuple, securityId); ++ + umId = simple_heap_insert(rel, tuple); + + CatalogUpdateIndexes(rel, tuple); +@@ -1000,6 +1033,9 @@ AlterUserMapping(AlterUserMappingStmt *stmt) + + user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername); + ++ /* SELinux checks */ ++ sepgsql_user_mapping_alter(umId); ++ + tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId)); + + if (!HeapTupleIsValid(tp)) +@@ -1114,6 +1150,9 @@ RemoveUserMapping(DropUserMappingStmt *stmt) + + user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername); + ++ /* SELinux checks */ ++ sepgsql_user_mapping_drop(umId, false); ++ + /* + * Do the deletion + */ +diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c +index 9a584ed..f914280 100644 +--- a/src/backend/commands/functioncmds.c ++++ b/src/backend/commands/functioncmds.c +@@ -43,6 +43,7 @@ + #include "catalog/pg_namespace.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_proc_fn.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_type.h" + #include "catalog/pg_type_fn.h" + #include "commands/defrem.h" +@@ -53,6 +54,7 @@ + #include "parser/parse_expr.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -788,6 +790,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) + ArrayType *parameterNames; + List *parameterDefaults; + Oid requiredResultType; ++ Oid replacedFunc = InvalidOid; ++ Oid prosecid; + bool isWindowFunc, + isStrict, + security; +@@ -933,6 +937,18 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) + errmsg("ROWS is not applicable when function does not return a set"))); + + /* ++ * SELinux checks ++ */ ++ if (stmt->replace) ++ replacedFunc = GetSysCacheOid3(PROCNAMEARGSNSP, ++ PointerGetDatum(funcname), ++ PointerGetDatum(parameterTypes), ++ ObjectIdGetDatum(namespaceId)); ++ ++ prosecid = sepgsql_proc_create(funcname, replacedFunc, ++ namespaceId, languageOid); ++ ++ /* + * And now that we have all the parameters, and know we're permitted to do + * so, go ahead and create the function. + */ +@@ -957,7 +973,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) + parameterDefaults, + PointerGetDatum(proconfig), + procost, +- prorows); ++ prorows, ++ prosecid); + } + + +@@ -999,6 +1016,9 @@ RemoveFunction(RemoveFuncStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(functionName)); + ++ /* SELinux checks */ ++ sepgsql_proc_drop(funcOid, false); ++ + if (((Form_pg_proc) GETSTRUCT(tup))->proisagg) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), +@@ -1135,6 +1155,9 @@ RenameFunction(List *name, List *argtypes, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_proc_alter_rename(procOid, newname); ++ + /* rename */ + namestrcpy(&(procForm->proname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -1239,6 +1262,8 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(procForm->pronamespace)); + } ++ /* SELinux checks */ ++ sepgsql_proc_alter(procOid); + + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); +@@ -1277,6 +1302,49 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) + } + + /* ++ * ALTER FUNCTION f(...) SECURITY LABEL TO ++ */ ++void ++AlterFunctionSecLabel(List *name, List *argtypes, bool isagg, char *new_label) ++{ ++ Relation rel; ++ HeapTuple tuple; ++ Oid procOid; ++ Oid securityId; ++ ++ /* open pg_proc system catalog */ ++ rel = heap_open(ProcedureRelationId, RowExclusiveLock); ++ ++ /* get function OID */ ++ if (isagg) ++ procOid = LookupAggNameTypeNames(name, argtypes, false); ++ else ++ procOid = LookupFuncNameTypeNames(name, argtypes, false); ++ ++ tuple = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for function %u", procOid); ++ ++ /* Must be owner */ ++ if (!pg_proc_ownercheck(procOid, GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, ++ get_func_name(procOid)); ++ ++ /* SELinux checks */ ++ securityId = sepgsql_proc_relabel(procOid, new_label); ++ ++ /* Update it */ ++ HeapTupleSetSecid(tuple, securityId); ++ ++ simple_heap_update(rel, &tuple->t_self, tuple); ++ CatalogUpdateIndexes(rel, tuple); ++ ++ heap_freetuple(tuple); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++/* + * Implements the ALTER FUNCTION utility command (except for the + * RENAME and OWNER clauses, which are handled as part of the generic + * ALTER framework). +@@ -1313,6 +1381,9 @@ AlterFunction(AlterFunctionStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(stmt->func->funcname)); + ++ /* SELinux checks */ ++ sepgsql_proc_alter(funcOid); ++ + if (procForm->proisagg) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), +@@ -1659,6 +1730,9 @@ CreateCast(CreateCastStmt *stmt) + errmsg("array data types are not binary-compatible"))); + } + ++ /* SELinux checks */ ++ sepgsql_cast_create(sourcetypeid, targettypeid, castmethod, funcid); ++ + /* + * Allow source and target types to be same only for length coercion + * functions. We assume a multi-arg function does length coercion. +@@ -1795,6 +1869,9 @@ DropCast(DropCastStmt *stmt) + format_type_be(sourcetypeid), + format_type_be(targettypeid)))); + ++ /* SELinux checks */ ++ sepgsql_cast_drop(sourcetypeid, targettypeid, false); ++ + /* + * Do the deletion + */ +@@ -1873,6 +1950,9 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, + /* get schema OID and check its permissions */ + nspOid = LookupCreationNamespace(newschema); + ++ /* SELinux checks */ ++ sepgsql_proc_alter_schema(procOid, nspOid); ++ + if (oldNspOid == nspOid) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_FUNCTION), +diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c +index 94657b8..7cb3634 100644 +--- a/src/backend/commands/indexcmds.c ++++ b/src/backend/commands/indexcmds.c +@@ -39,6 +39,7 @@ + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parsetree.h" ++#include "sepgsql/hooks.h" + #include "storage/lmgr.h" + #include "storage/proc.h" + #include "storage/procarray.h" +@@ -242,6 +243,10 @@ DefineIndex(RangeVar *heapRelation, + get_tablespace_name(tablespaceId)); + } + ++ /* SELinux checks */ ++ if (check_rights) ++ sepgsql_index_create(relationId, namespaceId); ++ + /* + * Force shared indexes into the pg_global tablespace. This is a bit of a + * hack but seems simpler than marking them in the BKI commands. On the +@@ -363,7 +368,9 @@ DefineIndex(RangeVar *heapRelation, + errmsg("primary keys cannot be expressions"))); + + /* System attributes are never null, so no problem */ +- if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids)) ++ if (SystemAttributeByName(key->name, ++ rel->rd_rel->relhasoids, ++ rel->rd_rel->relhassecids)) + continue; + + atttuple = SearchSysCacheAttName(relationId, key->name); +@@ -1572,6 +1579,9 @@ ReindexIndex(RangeVar *indexRelation) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + indexRelation->relname); + ++ /* SELinux checks */ ++ sepgsql_index_reindex(indOid); ++ + ReleaseSysCache(tuple); + + reindex_index(indOid, false); +@@ -1604,6 +1614,9 @@ ReindexTable(RangeVar *relation) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + relation->relname); + ++ /* SELinux checks */ ++ sepgsql_relation_reindex(heapOid); ++ + ReleaseSysCache(tuple); + + if (!reindex_relation(heapOid, true, false)) +@@ -1642,6 +1655,9 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + databaseName); + ++ /* SELinux checks */ ++ sepgsql_database_reindex(MyDatabaseId); ++ + /* + * Create a memory context that will survive forced transaction commits we + * do below. Since it is a child of PortalContext, it will go away +diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c +index 283947a..657ff95 100644 +--- a/src/backend/commands/lockcmds.c ++++ b/src/backend/commands/lockcmds.c +@@ -20,6 +20,7 @@ + #include "commands/lockcmds.h" + #include "miscadmin.h" + #include "parser/parse_clause.h" ++#include "sepgsql/hooks.h" + #include "storage/lmgr.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" +@@ -149,6 +150,9 @@ LockTableRecurse(Oid reloid, RangeVar *rv, + errmsg("\"%s\" is not a table", + RelationGetRelationName(rel)))); + ++ /* SELinux checks */ ++ sepgsql_relation_lock(rel); ++ + /* + * If requested, recurse to children. We use find_inheritance_children + * not find_all_inheritors to avoid taking locks far in advance of +diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c +index ac0270f..25b84fd 100644 +--- a/src/backend/commands/opclasscmds.c ++++ b/src/backend/commands/opclasscmds.c +@@ -35,6 +35,7 @@ + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -164,7 +165,8 @@ OpClassCacheLookup(Oid amID, List *opclassname) + * Caller must have done permissions checks etc. already. + */ + static Oid +-CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) ++CreateOpFamily(char *amname, char *opfname, ++ Oid namespaceoid, Oid amoid, Oid securityId) + { + Oid opfamilyoid; + Relation rel; +@@ -204,6 +206,8 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) + + tup = heap_form_tuple(rel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + opfamilyoid = simple_heap_insert(rel, tup); + + CatalogUpdateIndexes(rel, tup); +@@ -369,11 +373,16 @@ DefineOpClass(CreateOpClassStmt *stmt) + } + else + { ++ Oid securityId; ++ ++ /* SELinux checks */ ++ securityId = sepgsql_opfamily_create(opcname, namespaceoid, amoid); ++ + /* + * Create it ... again no need for more permissions ... + */ + opfamilyoid = CreateOpFamily(stmt->amname, opcname, +- namespaceoid, amoid); ++ namespaceoid, amoid, securityId); + } + } + +@@ -505,6 +514,12 @@ DefineOpClass(CreateOpClassStmt *stmt) + stmt->amname))); + } + ++ /* SELinux checks */ ++ sepgsql_opfamily_alter(opfamilyoid, false, amoid, ++ operators, procedures); ++ sepgsql_opclass_create(opcname, namespaceoid, ++ typeoid, opfamilyoid, storageoid); ++ + rel = heap_open(OperatorClassRelationId, RowExclusiveLock); + + /* +@@ -650,6 +665,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) + NameData opfName; + ObjectAddress myself, + referenced; ++ Oid securityId; + + /* Convert list of names to a name and namespace */ + namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname, +@@ -701,6 +717,9 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) + errmsg("operator family \"%s\" for access method \"%s\" already exists", + opfname, stmt->amname))); + ++ /* SELinux checks */ ++ securityId = sepgsql_opfamily_create(opfname, namespaceoid, amoid); ++ + /* + * Okay, let's create the pg_opfamily entry. + */ +@@ -715,6 +734,8 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) + + tup = heap_form_tuple(rel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + opfamilyoid = simple_heap_insert(rel, tup); + + CatalogUpdateIndexes(rel, tup); +@@ -925,6 +946,9 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid, + break; + } + } ++ /* SELinux checks */ ++ sepgsql_opfamily_alter(opfamilyoid, false, amoid, ++ operators, procedures); + + /* + * Add tuples to pg_amop and pg_amproc tying in the operators and +@@ -1002,6 +1026,9 @@ AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid, + } + } + ++ /* SELinux checks */ ++ sepgsql_opfamily_alter(opfamilyoid, true, amoid, operators, procedures); ++ + /* + * Remove tuples from pg_amop and pg_amproc. + */ +@@ -1522,6 +1549,9 @@ RemoveOpClass(RemoveOpClassStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, + NameListToString(stmt->opclassname)); + ++ /* SELinux checks */ ++ sepgsql_opclass_drop(opcID, false); ++ + ReleaseSysCache(tuple); + + /* +@@ -1583,6 +1613,9 @@ RemoveOpFamily(RemoveOpFamilyStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, + NameListToString(stmt->opfamilyname)); + ++ /* SELinux checks */ ++ sepgsql_opfamily_drop(opfID, false); ++ + ReleaseSysCache(tuple); + + /* +@@ -1781,6 +1814,9 @@ RenameOpClass(List *name, const char *access_method, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_opclass_alter_rename(opcOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -1875,6 +1911,9 @@ RenameOpFamily(List *name, const char *access_method, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_opfamily_alter_rename(opfOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -1990,6 +2029,8 @@ AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_opclass_alter(HeapTupleGetOid(tup)); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +@@ -2112,7 +2153,8 @@ AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } +- ++ /* SELinux checks */ ++ sepgsql_opfamily_alter_owner(HeapTupleGetOid(tup), newOwnerId); + /* + * Modify the owner --- okay to scribble on tup because it's a copy + */ +diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c +index 9c07cf3..2e19448 100644 +--- a/src/backend/commands/operatorcmds.c ++++ b/src/backend/commands/operatorcmds.c +@@ -45,6 +45,7 @@ + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + #include "utils/rel.h" +@@ -319,6 +320,9 @@ RemoveOperator(RemoveFuncStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, + NameListToString(operatorName)); + ++ /* SELinux checks */ ++ sepgsql_operator_drop(operOid, false); ++ + ReleaseSysCache(tup); + + /* +@@ -426,6 +430,8 @@ AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(oprForm->oprnamespace)); + } ++ /* SELinux checks */ ++ sepgsql_operator_alter(operOid); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c +index e765382..a54dd3f 100644 +--- a/src/backend/commands/prepare.c ++++ b/src/backend/commands/prepare.c +@@ -759,7 +759,7 @@ pg_prepared_statement(PG_FUNCTION_ARGS) + * build tupdesc for result tuples. This must match the definition of the + * pg_prepared_statements view in system_views.sql + */ +- tupdesc = CreateTemplateTupleDesc(5, false); ++ tupdesc = CreateTemplateTupleDesc(5, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", +diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c +index 633a093..a3375b2 100644 +--- a/src/backend/commands/proclang.c ++++ b/src/backend/commands/proclang.c +@@ -29,6 +29,7 @@ + #include "miscadmin.h" + #include "parser/parse_func.h" + #include "parser/parser.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -145,7 +146,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) + NIL, + PointerGetDatum(NULL), + 1, +- 0); ++ 0, ++ InvalidOid); + } + + /* +@@ -180,7 +182,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) + NIL, + PointerGetDatum(NULL), + 1, +- 0); ++ 0, ++ InvalidOid); + } + } + else +@@ -218,7 +221,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) + NIL, + PointerGetDatum(NULL), + 1, +- 0); ++ 0, ++ InvalidOid); + } + } + else +diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c +index b0a9a22..57951fe 100644 +--- a/src/backend/commands/schemacmds.c ++++ b/src/backend/commands/schemacmds.c +@@ -21,10 +21,12 @@ + #include "catalog/indexing.h" + #include "catalog/namespace.h" + #include "catalog/pg_namespace.h" ++#include "catalog/pg_seclabel.h" + #include "commands/dbcommands.h" + #include "commands/schemacmds.h" + #include "miscadmin.h" + #include "parser/parse_utilcmd.h" ++#include "sepgsql/hooks.h" + #include "tcop/utility.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -49,6 +51,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) + Oid owner_uid; + Oid saved_uid; + int save_sec_context; ++ Oid secid; + AclResult aclresult; + + GetUserIdAndSecContext(&saved_uid, &save_sec_context); +@@ -75,6 +78,9 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) + + check_is_member_of_role(saved_uid, owner_uid); + ++ /* SELinux checks */ ++ secid = sepgsql_schema_create(schemaName, false); ++ + /* Additional check to protect reserved schema names */ + if (!allowSystemTableMods && IsReservedName(schemaName)) + ereport(ERROR, +@@ -95,7 +101,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); + + /* Create the schema's namespace */ +- namespaceId = NamespaceCreate(schemaName, owner_uid); ++ namespaceId = NamespaceCreate(schemaName, owner_uid, secid); + + /* Advance cmd counter to make the namespace visible */ + CommandCounterIncrement(); +@@ -204,6 +210,9 @@ RemoveSchemas(DropStmt *drop) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, + namespaceName); + ++ /* SELinux checks */ ++ sepgsql_schema_drop(namespaceId, false); ++ + object.classId = NamespaceRelationId; + object.objectId = namespaceId; + object.objectSubId = 0; +@@ -288,6 +297,9 @@ RenameSchema(const char *oldname, const char *newname) + errmsg("unacceptable schema name \"%s\"", newname), + errdetail("The prefix \"pg_\" is reserved for system schemas."))); + ++ /* SELinux checks */ ++ sepgsql_schema_alter(HeapTupleGetOid(tup)); ++ + /* rename */ + namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -389,6 +401,9 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_DATABASE, + get_database_name(MyDatabaseId)); + ++ /* SELinux checks */ ++ sepgsql_schema_alter(HeapTupleGetOid(tup)); ++ + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + +@@ -423,3 +438,43 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) + } + + } ++ ++/* ++ * ALTER SCHEMA SECURITY LABEL TO ++ */ ++void ++AlterSchemaSecLabel(const char *name, char *new_label) ++{ ++ Relation rel; ++ HeapTuple tuple; ++ Oid namespaceId; ++ Oid securityId; ++ ++ /* open pg_namespace relation */ ++ rel = heap_open(NamespaceRelationId, RowExclusiveLock); ++ tuple = SearchSysCacheCopy1(NAMESPACENAME, ++ CStringGetDatum(name)); ++ if (!HeapTupleIsValid(tuple)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_SCHEMA), ++ errmsg("schema \"%s\" does not exist", name))); ++ namespaceId = HeapTupleGetOid(tuple); ++ ++ /* DAC permission check */ ++ if (!pg_namespace_ownercheck(namespaceId, GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, name); ++ ++ /* SELinux checks */ ++ securityId = sepgsql_schema_relabel(namespaceId, new_label); ++ ++ /* Update it */ ++ HeapTupleSetSecid(tuple, securityId); ++ ++ simple_heap_update(rel, &tuple->t_self, tuple); ++ ++ CatalogUpdateIndexes(rel, tuple); ++ ++ heap_freetuple(tuple); ++ ++ heap_close(rel, RowExclusiveLock); ++} +diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c +index f52e1d8..ecff05d 100644 +--- a/src/backend/commands/sequence.c ++++ b/src/backend/commands/sequence.c +@@ -26,6 +26,7 @@ + #include "commands/tablecmds.h" + #include "miscadmin.h" + #include "nodes/makefuncs.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/proc.h" +@@ -331,6 +332,9 @@ AlterSequence(AlterSeqStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + stmt->sequence->relname); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(relid); ++ + /* do the work */ + AlterSequenceInternal(relid, stmt->options); + } +@@ -469,6 +473,9 @@ nextval_internal(Oid relid) + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux checks */ ++ sepgsql_sequence_next_value(elm->relid); ++ + /* read-only transactions may only modify temp sequences */ + if (!seqrel->rd_islocaltemp) + PreventCommandIfReadOnly("nextval()"); +@@ -668,6 +675,9 @@ currval_oid(PG_FUNCTION_ARGS) + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux checks */ ++ sepgsql_sequence_get_value(elm->relid); ++ + if (!elm->last_valid) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), +@@ -710,6 +720,9 @@ lastval(PG_FUNCTION_ARGS) + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux checks */ ++ sepgsql_sequence_get_value(last_used_seq->relid); ++ + result = last_used_seq->last; + relation_close(seqrel, NoLock); + +@@ -746,6 +759,9 @@ do_setval(Oid relid, int64 next, bool iscalled) + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux checks */ ++ sepgsql_sequence_set_value(elm->relid); ++ + /* read-only transactions may only modify temp sequences */ + if (!seqrel->rd_islocaltemp) + PreventCommandIfReadOnly("setval()"); +diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c +index 9b5ce65..34ffbe2 100644 +--- a/src/backend/commands/tablecmds.c ++++ b/src/backend/commands/tablecmds.c +@@ -32,6 +32,7 @@ + #include "catalog/pg_inherits_fn.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_opclass.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_trigger.h" + #include "catalog/pg_type.h" +@@ -62,12 +63,14 @@ + #include "parser/parser.h" + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteHandler.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/smgr.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" ++#include "utils/guc.h" + #include "utils/inval.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" +@@ -223,7 +226,7 @@ static const struct dropmsgstrings dropmsgstringarray[] = { + + static void truncate_check_rel(Relation rel); + static List *MergeAttributes(List *schema, List *supers, bool istemp, +- List **supOids, List **supconstr, int *supOidCount); ++ List **supOids, List **supconstr, int *supOidCount, int *supSecidCount); + static bool MergeCheckConstraint(List *constraints, char *name, Node *expr); + static bool change_varattnos_walker(Node *node, const AttrNumber *newattno); + static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel); +@@ -271,10 +274,12 @@ static void ATOneLevelRecursion(List **wqueue, Relation rel, + static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, + AlterTableCmd *cmd); + static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +- ColumnDef *colDef, bool isOid); ++ ColumnDef *colDef, bool isOid, bool isSecid); + static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid); + static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse, + AlterTableCmd *cmd); ++static void ATPrepAddSecLabel(List **wqueue, Relation rel, bool recurse, ++ AlterTableCmd *cmd); + static void ATExecDropNotNull(Relation rel, const char *colName); + static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, + const char *colName); +@@ -355,6 +360,7 @@ DefineRelation(CreateStmt *stmt, char relkind) + List *old_constraints; + bool localHasOids; + int parentOidCount; ++ int parentSecidCount; + List *rawDefaults; + List *cookedDefaults; + Datum reloptions; +@@ -362,6 +368,7 @@ DefineRelation(CreateStmt *stmt, char relkind) + AttrNumber attnum; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; + Oid ofTypeId; ++ Oid *secLabels; + + /* + * Truncate relname to appropriate length (probably a waste of time, as +@@ -461,7 +468,8 @@ DefineRelation(CreateStmt *stmt, char relkind) + */ + schema = MergeAttributes(schema, stmt->inhRelations, + stmt->relation->istemp, +- &inheritOids, &old_constraints, &parentOidCount); ++ &inheritOids, &old_constraints, ++ &parentOidCount, &parentSecidCount); + + /* + * Create a tuple descriptor from the relation schema. Note that this +@@ -473,6 +481,16 @@ DefineRelation(CreateStmt *stmt, char relkind) + localHasOids = interpretOidsOption(stmt->options); + descriptor->tdhasoid = (localHasOids || parentOidCount > 0); + ++ if ((relkind == RELKIND_RELATION && default_with_secids) || parentSecidCount > 0) ++ descriptor->tdhassecid = true; ++ ++ /* SELinux permission checks */ ++ secLabels = sepgsql_relation_create(relname, ++ relkind, ++ descriptor, ++ namespaceId, ++ inheritOids, ++ false); + /* + * Find columns with default values and prepare for insertion of the + * defaults. Pre-cooked (that is, inherited) defaults go into a list of +@@ -546,7 +564,8 @@ DefineRelation(CreateStmt *stmt, char relkind) + stmt->oncommit, + reloptions, + true, +- allowSystemTableMods); ++ allowSystemTableMods, ++ secLabels); + + StoreCatalogInheritance(relationId, inheritOids); + +@@ -755,6 +774,9 @@ RemoveRelations(DropStmt *drop) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + rel->relname); + ++ /* SELinux checks */ ++ sepgsql_relation_drop(relOid, false); ++ + if (!allowSystemTableMods && IsSystemClass(classform)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), +@@ -918,6 +940,9 @@ ExecuteTruncate(TruncateStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(seq_rel)); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(seq_relid); ++ + seq_relids = lappend_oid(seq_relids, seq_relid); + + relation_close(seq_rel, NoLock); +@@ -1086,6 +1111,8 @@ truncate_check_rel(Relation rel) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_relation_truncate(rel); + + if (!allowSystemTableMods && IsSystemRelation(rel)) + ereport(ERROR, +@@ -1146,6 +1173,7 @@ storage_name(char c) + * 'supconstr' receives a list of constraints belonging to the parents, + * updated as necessary to be valid for the child. + * 'supOidCount' is set to the number of parents that have OID columns. ++ * 'supSecidCount' is set to the number of parents that have SID columns. + * + * Return value: + * Completed schema list. +@@ -1191,13 +1219,15 @@ storage_name(char c) + */ + static List * + MergeAttributes(List *schema, List *supers, bool istemp, +- List **supOids, List **supconstr, int *supOidCount) ++ List **supOids, List **supconstr, ++ int *supOidCount, int *supSecidCount) + { + ListCell *entry; + List *inhSchema = NIL; + List *parentOids = NIL; + List *constraints = NIL; + int parentsWithOids = 0; ++ int parentsWithSecids = 0; + bool have_bogus_defaults = false; + int child_attno; + static Node bogus_marker = {0}; /* marks conflicting defaults */ +@@ -1325,6 +1355,8 @@ MergeAttributes(List *schema, List *supers, bool istemp, + + if (relation->rd_rel->relhasoids) + parentsWithOids++; ++ if (relation->rd_rel->relhassecids) ++ parentsWithSecids++; + + tupleDesc = RelationGetDescr(relation); + constr = tupleDesc->constr; +@@ -1626,6 +1658,7 @@ MergeAttributes(List *schema, List *supers, bool istemp, + *supOids = parentOids; + *supconstr = constraints; + *supOidCount = parentsWithOids; ++ *supSecidCount = parentsWithSecids; + return schema; + } + +@@ -1985,6 +2018,9 @@ renameatt(Oid myrelid, + errmsg("permission denied: \"%s\" is a system catalog", + RelationGetRelationName(targetrelation)))); + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(myrelid, oldattname); ++ + /* + * if the 'recurse' flag is set then we are supposed to rename this + * attribute in all classes that inherit from 'relname' (as well as in +@@ -2136,6 +2172,9 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype) + errmsg("\"%s\" is not a view", + RelationGetRelationName(targetrelation)))); + ++ /* SELinux checks */ ++ sepgsql_relation_alter_rename(myrelid, newrelname); ++ + /* + * Don't allow ALTER TABLE on composite types. We want people to use ALTER + * TYPE for that. +@@ -2566,6 +2605,27 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, + } + pass = AT_PASS_DROP; + break; ++ case AT_AddSecLabel: ++ ATSimplePermissions(rel, false); ++ /* Performs own recursion */ ++ if (!rel->rd_rel->relhassecids || recursing) ++ ATPrepAddSecLabel(wqueue, rel, recurse, cmd); ++ pass = AT_PASS_ADD_COL; ++ break; ++ case AT_DropSecLabel: ++ ATSimplePermissions(rel, false); ++ /* Performs own recursion */ ++ if (rel->rd_rel->relhassecids) ++ { ++ AlterTableCmd *dropCmd = makeNode(AlterTableCmd); ++ ++ dropCmd->subtype = AT_DropColumn; ++ dropCmd->name = pstrdup("security_label"); ++ dropCmd->behavior = cmd->behavior; ++ ATPrepCmd(wqueue, rel, dropCmd, recurse, false); ++ } ++ pass = AT_PASS_DROP; ++ break; + case AT_SetTableSpace: /* SET TABLESPACE */ + ATSimplePermissionsRelationOrIndex(rel); + /* This command never recurses */ +@@ -2690,7 +2750,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, + case AT_AddColumn: /* ADD COLUMN */ + case AT_AddColumnToView: /* add column via CREATE OR REPLACE + * VIEW */ +- ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false); ++ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false, false); + break; + case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */ + ATExecColumnDefault(rel, cmd->name, cmd->def); +@@ -2762,17 +2822,22 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, + case AT_AddOids: /* SET WITH OIDS */ + /* Use the ADD COLUMN code, unless prep decided to do nothing */ + if (cmd->def != NULL) +- ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true); ++ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true, false); ++ break; ++ case AT_AddSecLabel: /* SET WITH SECURITY LABEL */ ++ /* Use the ADD COLUMN code, unless prep decided to do nothing */ ++ if (cmd->def != NULL) ++ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false, true); + break; + case AT_DropOids: /* SET WITHOUT OIDS */ +- ++ case AT_DropSecLabel: /* SET WITHOUT SECURITY LABEL */ + /* + * Nothing to do here; we'll have generated a DropColumn + * subcommand to do the real work + */ + break; + case AT_SetTableSpace: /* SET TABLESPACE */ +- ++ sepgsql_relation_alter(RelationGetRelid(rel)); + /* + * Nothing to do here; Phase 3 does the work + */ +@@ -3126,6 +3191,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) + MemoryContext oldCxt; + List *dropped_attrs = NIL; + ListCell *lc; ++ Oid tupSecidInherit = InvalidOid; + + econtext = GetPerTupleExprContext(estate); + +@@ -3156,6 +3222,19 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) + } + + /* ++ * If this routine is called due to ALTER TABLE SET WITH SECURITY LABEL, ++ * security label of the relation shall be assigned tuples. ++ */ ++ if (!oldTupDesc->tdhassecid && newTupDesc->tdhassecid) ++ { ++ Oid relSecid = GetSysCacheSecid1(RELOID, ++ ObjectIdGetDatum(tab->relid)); ++ ++ tupSecidInherit = seclabelMoveSecid(RelationGetRelid(oldrel), ++ RelationRelationId, relSecid); ++ } ++ ++ /* + * Scan through the rows, generating a new row if needed and then + * checking all the constraints. + */ +@@ -3172,11 +3251,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) + if (newrel) + { + Oid tupOid = InvalidOid; ++ Oid tupSecid = InvalidOid; + + /* Extract data from old tuple */ + heap_deform_tuple(tuple, oldTupDesc, values, isnull); + if (oldTupDesc->tdhasoid) + tupOid = HeapTupleGetOid(tuple); ++ if (oldTupDesc->tdhassecid) ++ tupSecid = HeapTupleGetSecid(tuple); ++ else ++ tupSecid = tupSecidInherit; + + /* Set dropped attributes to null in new tuple */ + foreach(lc, dropped_attrs) +@@ -3208,6 +3292,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) + /* Preserve OID, if any */ + if (newTupDesc->tdhasoid) + HeapTupleSetOid(tuple, tupOid); ++ /* Preserve security-id, if any */ ++ if (newTupDesc->tdhassecid) ++ HeapTupleSetSecid(tuple, tupSecid); + } + + /* Now check any constraints on the possibly-changed tuple */ +@@ -3601,7 +3688,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, + + static void + ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +- ColumnDef *colDef, bool isOid) ++ ColumnDef *colDef, bool isOid, bool isSecid) + { + Oid myrelid = RelationGetRelid(rel); + Relation pgclass, +@@ -3615,12 +3702,16 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + int32 typmod; + Form_pg_type tform; + Expr *defval; ++ Oid securityId; + + if (rel->rd_rel->reloftype) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot add column to typed table"))); + ++ /* SELinux permission check */ ++ securityId = sepgsql_attribute_create(myrelid, colDef->colname); ++ + attrdesc = heap_open(AttributeRelationId, RowExclusiveLock); + + /* +@@ -3648,6 +3739,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + errmsg("child table \"%s\" has different type for column \"%s\"", + RelationGetRelationName(rel), colDef->colname))); + ++ if (!seclabelCompareSecid(AttributeRelationId, securityId, ++ AttributeRelationId, HeapTupleGetSecid(tuple))) ++ ereport(ERROR, ++ (errcode(ERRCODE_DATATYPE_MISMATCH), ++ errmsg("child table \"%s\" has different label for column \"%s\"", ++ RelationGetRelationName(rel), colDef->colname))); ++ + /* If it's OID, child column must actually be OID */ + if (isOid && childatt->attnum != ObjectIdAttributeNumber) + ereport(ERROR, +@@ -3655,6 +3753,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + errmsg("child table \"%s\" has a conflicting \"%s\" column", + RelationGetRelationName(rel), colDef->colname))); + ++ /* If it's SecID, child column must actually be SecID */ ++ if (isSecid && childatt->attnum != SecurityLabelAttributeNumber) ++ ereport(ERROR, ++ (errcode(ERRCODE_DATATYPE_MISMATCH), ++ errmsg("child table \"%s\" has a conflicting \"%s\" column", ++ RelationGetRelationName(rel), colDef->colname))); ++ + /* Bump the existing child att's inhcount */ + childatt->attinhcount++; + simple_heap_update(attrdesc, &tuple->t_self, tuple); +@@ -3694,6 +3799,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + /* Determine the new attribute's number */ + if (isOid) + newattnum = ObjectIdAttributeNumber; ++ else if (isSecid) ++ newattnum = SecurityLabelAttributeNumber; + else + { + newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1; +@@ -3733,7 +3840,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + + ReleaseSysCache(typeTuple); + +- InsertPgAttributeTuple(attrdesc, &attribute, NULL); ++ InsertPgAttributeTuple(attrdesc, &attribute, NULL, securityId); + + heap_close(attrdesc, RowExclusiveLock); + +@@ -3742,6 +3849,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + */ + if (isOid) + ((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true; ++ else if (isSecid) ++ ((Form_pg_class) GETSTRUCT(reltup))->relhassecids = true; + else + ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum; + +@@ -3853,7 +3962,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, + * If we are adding an OID column, we have to tell Phase 3 to rewrite the + * table to fix that. + */ +- if (isOid) ++ if (isOid || isSecid) + tab->new_changeoids = true; + + /* +@@ -3906,6 +4015,31 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) + } + + /* ++ * ALTER TABLE SET WITH SECURITY LABEL ++ * ++ * Basically this is an ADD COLUMN for the special SecLabel column. ++ * We have to cons up a ColumnDef node because the ADD COLUMN code needs one. ++ */ ++static void ++ATPrepAddSecLabel(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) ++{ ++ /* If we're recursing to a child table, the ColumnDef is already set up */ ++ if (cmd->def == NULL) ++ { ++ ColumnDef *cdef = makeNode(ColumnDef); ++ ++ cdef->colname = pstrdup("security_label"); ++ cdef->typeName = makeTypeNameFromOid(TEXTOID, -1); ++ cdef->inhcount = 0; ++ cdef->is_local = true; ++ cdef->is_not_null = true; ++ cdef->storage = 0; ++ cmd->def = (Node *) cdef; ++ } ++ ATPrepAddColumn(wqueue, rel, recurse, cmd); ++} ++ ++/* + * ALTER TABLE ALTER COLUMN DROP NOT NULL + */ + static void +@@ -3917,6 +4051,9 @@ ATExecDropNotNull(Relation rel, const char *colName) + List *indexoidlist; + ListCell *indexoidscan; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + /* + * lookup the attribute + */ +@@ -4007,6 +4144,9 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, + AttrNumber attnum; + Relation attr_rel; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + /* + * lookup the attribute + */ +@@ -4057,6 +4197,9 @@ ATExecColumnDefault(Relation rel, const char *colName, + { + AttrNumber attnum; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + /* + * get the number of the attribute + */ +@@ -4131,6 +4274,9 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue) + HeapTuple tuple; + Form_pg_attribute attrtuple; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + Assert(IsA(newValue, Integer)); + newtarget = intVal(newValue); + +@@ -4197,6 +4343,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, + bool repl_null[Natts_pg_attribute]; + bool repl_repl[Natts_pg_attribute]; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + attrelation = heap_open(AttributeRelationId, RowExclusiveLock); + + tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName); +@@ -4256,6 +4405,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue) + HeapTuple tuple; + Form_pg_attribute attrtuple; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + Assert(IsA(newValue, String)); + storagemode = strVal(newValue); + +@@ -4346,6 +4498,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, + if (recursing) + ATSimplePermissions(rel, false); + ++ /* SELinux checks */ ++ sepgsql_attribute_drop(RelationGetRelid(rel), colName, false); ++ + /* + * get the number of the attribute + */ +@@ -4371,8 +4526,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, + + attnum = targetatt->attnum; + +- /* Can't drop a system attribute, except OID */ +- if (attnum <= 0 && attnum != ObjectIdAttributeNumber) ++ /* Can't drop a system attribute, except OID/SecID */ ++ if (attnum <= 0 && ++ attnum != ObjectIdAttributeNumber && ++ attnum != SecurityLabelAttributeNumber) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot drop system column \"%s\"", +@@ -4488,7 +4645,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, + * If we dropped the OID column, must adjust pg_class.relhasoids and tell + * Phase 3 to physically get rid of the column. + */ +- if (attnum == ObjectIdAttributeNumber) ++ if (attnum == ObjectIdAttributeNumber || ++ attnum == SecurityLabelAttributeNumber) + { + Relation class_rel; + Form_pg_class tuple_class; +@@ -4503,7 +4661,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, + RelationGetRelid(rel)); + tuple_class = (Form_pg_class) GETSTRUCT(tuple); + +- tuple_class->relhasoids = false; ++ if (attnum == ObjectIdAttributeNumber) ++ tuple_class->relhasoids = false; ++ if (attnum == SecurityLabelAttributeNumber) ++ tuple_class->relhassecids = false; ++ + simple_heap_update(class_rel, &tuple->t_self, tuple); + + /* Keep the catalog indexes up to date */ +@@ -4650,6 +4812,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, + if (recursing) + ATSimplePermissions(rel, false); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + /* + * Call AddRelationNewConstraints to do the work, making sure it works on + * a copy of the Constraint so transformExpr can't modify the original. It +@@ -4847,6 +5012,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, + checkFkeyPermissions(pkrel, pkattnum, numpks); + checkFkeyPermissions(rel, fkattnum, numfks); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + /* + * Look up the equality operators to use in the constraint. + * +@@ -5592,6 +5760,9 @@ ATExecDropConstraint(Relation rel, const char *constrName, + if (recursing) + ATSimplePermissions(rel, false); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + + /* +@@ -5924,6 +6095,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, + SysScanDesc scan; + HeapTuple depTup; + ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + attrelation = heap_open(AttributeRelationId, RowExclusiveLock); + + /* Look up the target column */ +@@ -6537,6 +6711,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_relation_alter(relationOid); + } + + memset(repl_null, false, sizeof(repl_null)); +@@ -6702,6 +6878,9 @@ ATExecClusterOn(Relation rel, const char *indexName) + { + Oid indexOid; + ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace); + + if (!OidIsValid(indexOid)) +@@ -6726,6 +6905,9 @@ ATExecClusterOn(Relation rel, const char *indexName) + static void + ATExecDropCluster(Relation rel) + { ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + mark_index_clustered(rel, InvalidOid); + } + +@@ -6776,6 +6958,9 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) + bool repl_repl[Natts_pg_class]; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; + ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + if (defList == NIL) + return; /* nothing to do */ + +@@ -7090,6 +7275,9 @@ static void + ATExecEnableDisableTrigger(Relation rel, char *trigname, + char fires_when, bool skip_system) + { ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + EnableDisableTrigger(rel, trigname, fires_when, skip_system); + } + +@@ -7102,6 +7290,9 @@ static void + ATExecEnableDisableRule(Relation rel, char *trigname, + char fires_when) + { ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + EnableDisableRule(rel, trigname, fires_when); + } + +@@ -7135,6 +7326,10 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent) + */ + ATSimplePermissions(parent_rel, false); + ++ /* SELinux checks */ ++ sepgsql_relation_alter_inherit(RelationGetRelid(child_rel), ++ RelationGetRelid(parent_rel)); ++ + /* Permanent rels cannot inherit from temporary ones */ + if (parent_rel->rd_istemp && !child_rel->rd_istemp) + ereport(ERROR, +@@ -7487,6 +7682,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent) + List *connames; + bool found = false; + ++ /* SELinux checks */ ++ sepgsql_relation_alter(RelationGetRelid(rel)); ++ + /* + * AccessShareLock on the parent is probably enough, seeing that DROP + * TABLE doesn't lock parent tables at all. We need some lock since we'll +@@ -7784,6 +7982,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, + /* get schema OID and check its permissions */ + nspOid = LookupCreationNamespace(newschema); + ++ /* SELinux checks */ ++ sepgsql_relation_alter_schema(RelationGetRelid(rel), nspOid); ++ + if (oldNspOid == nspOid) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_TABLE), +@@ -7981,6 +8182,282 @@ AlterSeqNamespaces(Relation classRel, Relation rel, + relation_close(depRel, AccessShareLock); + } + ++/* ++ * ALTER TABLE/SEQUENCE/VIEW SECURITY LABEL TO ++ */ ++void ++AlterRelationSecLabelInternal(Oid relOid, Oid securityId, int expected_parents) ++{ ++ Form_pg_class classForm; ++ Relation targetRel; ++ Relation inhRel; ++ Relation classRel; ++ SysScanDesc scan; ++ ScanKeyData skey; ++ HeapTuple tuple; ++ List *indexList; ++ ListCell *cell; ++ int inhcount = 0; ++ ++ /* ++ * Grab an exclusive lock on the target table ++ */ ++ targetRel = relation_open(relOid, AccessExclusiveLock); ++ ++ /* ++ * Check num of inheritors ++ */ ++ inhRel = heap_open(InheritsRelationId, AccessShareLock); ++ ++ ScanKeyInit(&skey, ++ Anum_pg_inherits_inhrelid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relOid)); ++ ++ scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, ++ true, SnapshotNow, 1, &skey); ++ while (HeapTupleIsValid(systable_getnext(scan))) ++ inhcount++; ++ ++ systable_endscan(scan); ++ ++ heap_close(inhRel, AccessShareLock); ++ ++ if (inhcount != expected_parents) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_TABLE_DEFINITION), ++ errmsg("cannot relabel inherited relation"))); ++ ++ /* ++ * Update pg_class relation ++ */ ++ classRel = heap_open(RelationRelationId, RowExclusiveLock); ++ ++ tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relOid); ++ ++ classForm = (Form_pg_class) GETSTRUCT(tuple); ++ ++ HeapTupleSetSecid(tuple, securityId); ++ ++ simple_heap_update(classRel, &tuple->t_self, tuple); ++ ++ CatalogUpdateIndexes(classRel, tuple); ++ ++ heap_close(classRel, RowExclusiveLock); ++ ++ /* ++ * Also update TOAST and INDEX ++ */ ++ if (OidIsValid(classForm->reltoastrelid)) ++ AlterRelationSecLabelInternal(classForm->reltoastrelid, ++ securityId, 0); ++ ++ indexList = RelationGetIndexList(targetRel); ++ foreach (cell, indexList) ++ AlterRelationSecLabelInternal(lfirst_oid(cell), securityId, 0); ++ ++ /* ++ * Also update pg_attribute, if not RELKIND_RELATION ++ */ ++ if (classForm->relkind != RELKIND_RELATION) ++ { ++ Relation attRel; ++ HeapTuple oldtup, newtup; ++ Oid attsecid; ++ ++ /* move security-id to pg_attribtue catalog */ ++ attsecid = seclabelMoveSecid(AttributeRelationId, ++ RelationRelationId, ++ securityId); ++ ++ attRel = heap_open(AttributeRelationId, RowExclusiveLock); ++ ++ ScanKeyInit(&skey, ++ Anum_pg_attribute_attrelid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(RelationGetRelid(targetRel))); ++ ++ scan = systable_beginscan(attRel, AttributeRelidNumIndexId, true, ++ SnapshotNow, 1, &skey); ++ while (HeapTupleIsValid(oldtup = systable_getnext(scan))) ++ { ++ Form_pg_attribute attForm ++ = (Form_pg_attribute) GETSTRUCT(oldtup); ++ ++ if (attForm->attinhcount > 0) ++ elog(ERROR, "Bug? attinhcount is %d at %s of %s", ++ attForm->attinhcount, NameStr(attForm->attname), ++ RelationGetRelationName(targetRel)); ++ ++ newtup = heap_copytuple(oldtup); ++ ++ HeapTupleSetSecid(newtup, attsecid); ++ ++ simple_heap_update(attRel, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(attRel, newtup); ++ } ++ systable_endscan(scan); ++ ++ heap_close(attRel, RowExclusiveLock); ++ } ++ ++ heap_close(targetRel, NoLock); /* close rel but keep lock */ ++} ++ ++void ++AlterAttributeSecLabelInternal(Oid relOid, const char *attname, ++ Oid securityId, int expected_parents) ++{ ++ Form_pg_attribute attForm; ++ Relation targetRel; ++ Relation attRel; ++ HeapTuple tuple; ++ ++ /* ++ * Grab an exclusive lock on the target table, which we will NOT ++ * release until end of transaction. ++ */ ++ targetRel = heap_open(relOid, AccessExclusiveLock); ++ ++ attRel = heap_open(AttributeRelationId, RowExclusiveLock); ++ ++ tuple = SearchSysCacheCopyAttName(relOid, attname); ++ if (!HeapTupleIsValid(tuple)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_COLUMN), ++ errmsg("column \"%s\" does not exist", attname))); ++ ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++#if 0 ++ /* ++ * XXX - here is no active reason why we forbid to relabel ++ * system columns. ++ */ ++ if (attForm->attnum <= 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("cannot relabel system column \"%s\"", attname))); ++#endif ++ if (attForm->attinhcount > expected_parents) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_TABLE_DEFINITION), ++ errmsg("cannot relabel inherited column \"%s\"", attname))); ++ ++ /* update pg_attribute */ ++ HeapTupleSetSecid(tuple, securityId); ++ ++ simple_heap_update(attRel, &tuple->t_self, tuple); ++ ++ CatalogUpdateIndexes(attRel, tuple); ++ ++ heap_close(attRel, RowExclusiveLock); ++ ++ heap_close(targetRel, NoLock); /* close rel but keep lock */ ++} ++ ++void ++AlterRelationSecLabel(RangeVar *relation, const char *attname, ++ ObjectType objtype, char *new_label) ++{ ++ Oid relOid = RangeVarGetRelid(relation, false); ++ Oid securityId; ++ char relkind; ++ List *child_oids, *child_numparents; ++ ListCell *lo, *li; ++ ++ /* ++ * Sanity checks for relation types ++ */ ++ relkind = get_rel_relkind(relOid); ++ switch (objtype) ++ { ++ case OBJECT_TABLE: ++ Assert(attname == NULL); ++ if (relkind != RELKIND_RELATION && ++ relkind != RELKIND_SEQUENCE && ++ relkind != RELKIND_VIEW) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a table, sequence or view", ++ get_rel_name(relOid)))); ++ break; ++ ++ case OBJECT_SEQUENCE: ++ Assert(attname == NULL); ++ if (relkind != RELKIND_SEQUENCE) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a sequence", ++ get_rel_name(relOid)))); ++ break; ++ ++ case OBJECT_VIEW: ++ Assert(attname == NULL); ++ if (relkind != RELKIND_VIEW) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a view", ++ get_rel_name(relOid)))); ++ break; ++ ++ case OBJECT_COLUMN: ++ Assert(attname != NULL); ++ if (relkind != RELKIND_RELATION) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a table", ++ get_rel_name(relOid)))); ++ break; ++ ++ default: ++ elog(ERROR, "Bug? unexpected object type %d", objtype); ++ break; ++ } ++ ++ /* ++ * Recursive calls to child relations including myself ++ */ ++ child_oids = find_all_inheritors(relOid, ++ AccessExclusiveLock, ++ &child_numparents); ++ forboth (lo, child_oids, li, child_numparents) ++ { ++ Oid childOid = lfirst_oid(lo); ++ int numParents = lfirst_int(li); ++ Oid relnsp = get_rel_namespace(childOid); ++ ++ /* Permission checks */ ++ if (!pg_class_ownercheck(childOid, GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, ++ get_rel_name(childOid)); ++ ++ if (!allowSystemTableMods && ++ (IsSystemNamespace(relnsp) || IsToastNamespace(relnsp))) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("permission denied: \"%s\" is a system catalog", ++ get_rel_name(childOid)))); ++ ++ /* SELinux checks */ ++ if (objtype != OBJECT_COLUMN) ++ securityId = sepgsql_relation_relabel(childOid, ++ new_label); ++ else ++ securityId = sepgsql_attribute_relabel(childOid, attname, ++ new_label); ++ ++ /* Do work */ ++ if (objtype != OBJECT_COLUMN) ++ AlterRelationSecLabelInternal(childOid, ++ securityId, numParents); ++ else ++ AlterAttributeSecLabelInternal(childOid, attname, ++ securityId, numParents); ++ } ++} + + /* + * This code supports +diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c +index 862cd2d..8ea4118 100644 +--- a/src/backend/commands/tablespace.c ++++ b/src/backend/commands/tablespace.c +@@ -65,6 +65,7 @@ + #include "commands/tablespace.h" + #include "miscadmin.h" + #include "postmaster/bgwriter.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "storage/procarray.h" + #include "storage/standby.h" +@@ -235,6 +236,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) + Oid tablespaceoid; + char *location; + Oid ownerId; ++ Oid securityId; + + /* Must be super user */ + if (!superuser()) +@@ -244,6 +246,9 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) + stmt->tablespacename), + errhint("Must be superuser to create a tablespace."))); + ++ /* SELinux check */ ++ securityId = sepgsql_tablespace_create(stmt->tablespacename); ++ + /* However, the eventual owner of the tablespace need not be */ + if (stmt->owner) + ownerId = get_roleid_checked(stmt->owner); +@@ -324,6 +329,8 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) + + tuple = heap_form_tuple(rel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tuple, securityId); ++ + tablespaceoid = simple_heap_insert(rel, tuple); + + CatalogUpdateIndexes(rel, tuple); +@@ -429,6 +436,9 @@ DropTableSpace(DropTableSpaceStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, + tablespacename); + ++ /* SELinux checks */ ++ sepgsql_tablespace_drop(tablespaceoid, false); ++ + /* Disallow drop of the standard tablespaces, even by superuser */ + if (tablespaceoid == GLOBALTABLESPACE_OID || + tablespaceoid == DEFAULTTABLESPACE_OID) +@@ -787,6 +797,9 @@ RenameTableSpace(const char *oldname, const char *newname) + if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId())) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, oldname); + ++ /* SELinux checks */ ++ sepgsql_tablespace_alter(HeapTupleGetOid(newtuple)); ++ + /* Validate new name */ + if (!allowSystemTableMods && IsReservedName(newname)) + ereport(ERROR, +@@ -868,6 +881,9 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId) + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), newOwnerId); + ++ /* SELinux checks */ ++ sepgsql_tablespace_alter(HeapTupleGetOid(tup)); ++ + /* + * Normally we would also check for create permissions here, but there + * are none for tablespaces so we follow what rename tablespace does +@@ -985,6 +1001,54 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt) + } + + /* ++ * ALTER TABLESPACE xxx SECURITY LABEL TO ... ++ */ ++void ++AlterTableSpaceSecLabel(const char *tspaceName, char *newLabel) ++{ ++ Relation rel; ++ ScanKeyData skey; ++ HeapScanDesc scan; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ Oid securityId; ++ ++ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); ++ ++ /* scan pg_tablespace catalog */ ++ ScanKeyInit(&skey, ++ Anum_pg_tablespace_spcname, ++ BTEqualStrategyNumber, F_NAMEEQ, ++ CStringGetDatum(tspaceName)); ++ scan = heap_beginscan(rel, SnapshotNow, 1, &skey); ++ oldtup = heap_getnext(scan, ForwardScanDirection); ++ if (!HeapTupleIsValid(oldtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("tablespace \"%s\" does not exist", tspaceName))); ++ ++ /* Must be owner */ ++ if (!pg_tablespace_ownercheck(HeapTupleGetOid(oldtup), GetUserId())) ++ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, tspaceName); ++ ++ /* SELinux checks */ ++ securityId = sepgsql_tablespace_relabel(HeapTupleGetSecid(oldtup), newLabel); ++ ++ /* update it */ ++ newtup = heap_copytuple(oldtup); ++ ++ HeapTupleSetSecid(newtup, securityId); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++} ++ ++/* + * Routines for handling the GUC variable 'default_tablespace'. + */ + +diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c +index 2cbc192..2ea9e6e 100644 +--- a/src/backend/commands/trigger.c ++++ b/src/backend/commands/trigger.c +@@ -40,6 +40,7 @@ + #include "parser/parsetree.h" + #include "pgstat.h" + #include "rewrite/rewriteManip.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "tcop/utility.h" + #include "utils/acl.h" +@@ -330,6 +331,10 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, + NameListToString(stmt->funcname)))); + } + ++ /* SELinux checks */ ++ sepgsql_trigger_create(RelationGetRelid(rel), stmt->trigname, ++ constrrelid, funcoid); ++ + /* + * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that + * references one of the built-in RI_FKey trigger functions, assume it is +@@ -1007,6 +1012,9 @@ DropTrigger(Oid relid, const char *trigname, DropBehavior behavior, + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(relid)); + ++ /* SELinux checks */ ++ sepgsql_trigger_drop(relid, trigname, false); ++ + object.classId = TriggerRelationId; + object.objectId = HeapTupleGetOid(tup); + object.objectSubId = 0; +@@ -1116,6 +1124,9 @@ renametrig(Oid relid, + SysScanDesc tgscan; + ScanKeyData key[2]; + ++ /* SELinux checks */ ++ sepgsql_trigger_alter(relid, oldname); ++ + /* + * Grab an exclusive lock on the target table, which we will NOT release + * until end of transaction. +diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c +index ba3de63..19bebb4 100644 +--- a/src/backend/commands/tsearchcmds.c ++++ b/src/backend/commands/tsearchcmds.c +@@ -35,6 +35,7 @@ + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" ++#include "sepgsql/hooks.h" + #include "tsearch/ts_cache.h" + #include "tsearch/ts_public.h" + #include "tsearch/ts_utils.h" +@@ -171,6 +172,7 @@ DefineTSParser(List *names, List *parameters) + NameData pname; + Oid prsOid; + Oid namespaceoid; ++ Oid securityId; + + if (!superuser()) + ereport(ERROR, +@@ -250,6 +252,14 @@ DefineTSParser(List *names, List *parameters) + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("text search parser lextypes method is required"))); + ++ /* SELinux checks */ ++ securityId = sepgsql_ts_parser_create(prsname, namespaceoid, ++ DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prsheadline - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])); ++ + /* + * Looks good, insert + */ +@@ -257,6 +267,8 @@ DefineTSParser(List *names, List *parameters) + + tup = heap_form_tuple(prsRel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + prsOid = simple_heap_insert(prsRel, tup); + + CatalogUpdateIndexes(prsRel, tup); +@@ -314,6 +326,8 @@ RemoveTSParsers(DropStmt *drop) + } + continue; + } ++ /* SELinux checks */ ++ sepgsql_ts_parser_drop(prsOid, false); + + object.classId = TSParserRelationId; + object.objectId = prsOid; +@@ -366,10 +380,13 @@ RenameTSParser(List *oldname, const char *newname) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to rename text search parsers"))); + +- rel = heap_open(TSParserRelationId, RowExclusiveLock); +- + prsId = TSParserGetPrsid(oldname, false); + ++ /* SELinux checks */ ++ sepgsql_ts_parser_alter_rename(prsId, newname); ++ ++ rel = heap_open(TSParserRelationId, RowExclusiveLock); ++ + tup = SearchSysCacheCopy1(TSPARSEROID, ObjectIdGetDatum(prsId)); + + if (!HeapTupleIsValid(tup)) /* should not happen */ +@@ -496,6 +513,7 @@ DefineTSDictionary(List *names, List *parameters) + List *dictoptions = NIL; + Oid dictOid; + Oid namespaceoid; ++ Oid securityId; + AclResult aclresult; + char *dictname; + +@@ -508,6 +526,9 @@ DefineTSDictionary(List *names, List *parameters) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceoid)); + ++ /* SELinux checks */ ++ securityId = sepgsql_ts_dict_create(dictname, namespaceoid); ++ + /* + * loop over the definition list and extract the information we need. + */ +@@ -557,6 +578,8 @@ DefineTSDictionary(List *names, List *parameters) + + tup = heap_form_tuple(dictRel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + dictOid = simple_heap_insert(dictRel, tup); + + CatalogUpdateIndexes(dictRel, tup); +@@ -610,6 +633,8 @@ RenameTSDictionary(List *oldname, const char *newname) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter_rename(dictId, newname); + + namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname); + simple_heap_update(rel, &tup->t_self, tup); +@@ -674,6 +699,8 @@ RemoveTSDictionaries(DropStmt *drop) + !pg_namespace_ownercheck(namespaceId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, + NameListToString(names)); ++ /* SELinux checks */ ++ sepgsql_ts_dict_drop(dictOid, false); + + object.classId = TSDictionaryRelationId; + object.objectId = dictOid; +@@ -745,6 +772,8 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt) + if (!pg_ts_dict_ownercheck(dictId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, + NameListToString(stmt->dictname)); ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId); + + /* deserialize the existing set of options */ + opt = SysCacheGetAttr(TSDICTOID, tup, +@@ -871,6 +900,8 @@ AlterTSDictionaryOwner(List *name, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId); + + form->dictowner = newOwnerId; + +@@ -982,6 +1013,7 @@ DefineTSTemplate(List *names, List *parameters) + int i; + Oid dictOid; + Oid namespaceoid; ++ Oid securityId; + char *tmplname; + + if (!superuser()) +@@ -1036,6 +1068,10 @@ DefineTSTemplate(List *names, List *parameters) + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("text search template lexize method is required"))); + ++ /* SELinux checks */ ++ securityId = sepgsql_ts_template_create(tmplname, namespaceoid, ++ DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])); + /* + * Looks good, insert + */ +@@ -1044,6 +1080,8 @@ DefineTSTemplate(List *names, List *parameters) + + tup = heap_form_tuple(tmplRel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + dictOid = simple_heap_insert(tmplRel, tup); + + CatalogUpdateIndexes(tmplRel, tup); +@@ -1075,6 +1113,9 @@ RenameTSTemplate(List *oldname, const char *newname) + + tmplId = TSTemplateGetTmplid(oldname, false); + ++ /* SELinux checks */ ++ sepgsql_ts_template_alter_rename(tmplId, newname); ++ + tup = SearchSysCacheCopy1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId)); + + if (!HeapTupleIsValid(tup)) /* should not happen */ +@@ -1145,6 +1186,8 @@ RemoveTSTemplates(DropStmt *drop) + } + continue; + } ++ /* SELinux checks */ ++ sepgsql_ts_template_drop(tmplOid, false); + + object.classId = TSTemplateRelationId; + object.objectId = tmplOid; +@@ -1305,6 +1348,7 @@ DefineTSConfiguration(List *names, List *parameters) + bool nulls[Natts_pg_ts_config]; + AclResult aclresult; + Oid namespaceoid; ++ Oid securityId; + char *cfgname; + NameData cname; + Oid sourceOid = InvalidOid; +@@ -1321,6 +1365,9 @@ DefineTSConfiguration(List *names, List *parameters) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceoid)); + ++ /* SELinux checks */ ++ securityId = sepgsql_ts_config_create(cfgname, namespaceoid); ++ + /* + * loop over the definition list and extract the information we need. + */ +@@ -1388,6 +1435,8 @@ DefineTSConfiguration(List *names, List *parameters) + + tup = heap_form_tuple(cfgRel->rd_att, values, nulls); + ++ HeapTupleSetSecid(tup, securityId); ++ + cfgOid = simple_heap_insert(cfgRel, tup); + + CatalogUpdateIndexes(cfgRel, tup); +@@ -1489,6 +1538,9 @@ RenameTSConfiguration(List *oldname, const char *newname) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_ts_config_alter_rename(cfgId, newname); ++ + namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); +@@ -1549,6 +1601,9 @@ RemoveTSConfigurations(DropStmt *drop) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, + NameListToString(names)); + ++ /* SELinux checks */ ++ sepgsql_ts_config_drop(cfgOid, false); ++ + object.classId = TSConfigRelationId; + object.objectId = cfgOid; + object.objectSubId = 0; +@@ -1656,6 +1711,8 @@ AlterTSConfigurationOwner(List *name, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_ts_config_alter(cfgId); + + form->cfgowner = newOwnerId; + +@@ -1693,6 +1750,9 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, + NameListToString(stmt->cfgname)); + ++ /* SELinux checks */ ++ sepgsql_ts_config_alter(HeapTupleGetOid(tup)); ++ + relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock); + + /* Add or drop mappings */ +diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c +index 1e14dca..f7429f0 100644 +--- a/src/backend/commands/typecmds.c ++++ b/src/backend/commands/typecmds.c +@@ -56,6 +56,7 @@ + #include "parser/parse_expr.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -143,6 +144,8 @@ DefineType(List *names, List *parameters) + char *array_type; + Oid array_oid; + Oid typoid; ++ Oid type_replaced; ++ Oid securityId; + Oid resulttype; + ListCell *pl; + +@@ -520,6 +523,15 @@ DefineType(List *names, List *parameters) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(analyzeName)); + #endif ++ /* SELinux checks */ ++ type_replaced = GetSysCacheOid2(TYPENAMENSP, ++ CStringGetDatum(typeName), ++ ObjectIdGetDatum(typeNamespace)); ++ securityId = sepgsql_type_create(typeName, type_replaced, ++ typeNamespace, TYPTYPE_BASE, ++ inputOid, outputOid, ++ receiveOid, sendOid, ++ typmodinOid, typmodoutOid, analyzeOid); + + array_oid = AssignTypeArrayOid(); + +@@ -562,7 +574,8 @@ DefineType(List *names, List *parameters) + storage, /* TOAST strategy */ + -1, /* typMod (Domains only) */ + 0, /* Array Dimensions of typbasetype */ +- false); /* Type NOT NULL */ ++ false, /* Type NOT NULL */ ++ securityId); /* security-id of the type */ + + /* + * Create the array type that goes with it. +@@ -601,7 +614,8 @@ DefineType(List *names, List *parameters) + 'x', /* ARRAY is always toastable */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ +- false); /* Type NOT NULL */ ++ false, /* Type NOT NULL */ ++ securityId); /* security-id of the type */ + + pfree(array_type); + } +@@ -668,6 +682,9 @@ RemoveTypes(DropStmt *drop) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, + format_type_be(typeoid)); + ++ /* SELinux checks */ ++ sepgsql_type_drop(typeoid, false); ++ + if (drop->removeType == OBJECT_DOMAIN) + { + /* Check that this is actually a domain */ +@@ -766,6 +783,7 @@ DefineDomain(CreateDomainStmt *stmt) + Oid basetypeoid; + Oid domainoid; + Oid old_type_oid; ++ Oid securityId; + Form_pg_type baseType; + int32 basetypeMod; + +@@ -1011,6 +1029,13 @@ DefineDomain(CreateDomainStmt *stmt) + } + } + ++ /* SELinux checks */ ++ securityId = sepgsql_type_create(domainName, InvalidOid, ++ domainNamespace, TYPTYPE_DOMAIN, ++ inputProcedure, outputProcedure, ++ receiveProcedure, sendProcedure, ++ InvalidOid, InvalidOid, analyzeProcedure); ++ + /* + * Have TypeCreate do all the real work. + */ +@@ -1044,7 +1069,8 @@ DefineDomain(CreateDomainStmt *stmt) + storage, /* TOAST strategy */ + basetypeMod, /* typeMod value */ + typNDims, /* Array dimensions for base type */ +- typNotNull); /* Type NOT NULL */ ++ typNotNull, /* Type NOT NULL */ ++ securityId); /* security-id of the type */ + + /* + * Process constraints which refer to the domain ID returned by TypeCreate +@@ -1094,6 +1120,7 @@ DefineEnum(CreateEnumStmt *stmt) + AclResult aclresult; + Oid old_type_oid; + Oid enumArrayOid; ++ Oid securityId; + + /* Convert list of names to a name and namespace */ + enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName, +@@ -1120,6 +1147,13 @@ DefineEnum(CreateEnumStmt *stmt) + errmsg("type \"%s\" already exists", enumName))); + } + ++ /* SELinux checks */ ++ securityId = sepgsql_type_create(enumName, old_type_oid, ++ enumNamespace, TYPTYPE_ENUM, ++ F_ENUM_IN, F_ENUM_OUT, ++ F_ENUM_RECV, F_ENUM_SEND, ++ InvalidOid, InvalidOid, InvalidOid); ++ + enumArrayOid = AssignTypeArrayOid(); + + /* Create the pg_type entry */ +@@ -1153,7 +1187,8 @@ DefineEnum(CreateEnumStmt *stmt) + 'p', /* TOAST strategy always plain */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ +- false); /* Type NOT NULL */ ++ false, /* Type NOT NULL */ ++ securityId); /* security-id of the type */ + + /* Enter the enum's values into pg_enum */ + EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid); +@@ -1192,7 +1227,8 @@ DefineEnum(CreateEnumStmt *stmt) + 'x', /* ARRAY is always toastable */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ +- false); /* Type NOT NULL */ ++ false, /* Type NOT NULL */ ++ securityId); /* security-id of the type */ + + pfree(enumArrayName); + } +@@ -1585,6 +1621,8 @@ AlterDomainDefault(List *names, Node *defaultRaw) + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); + ++ sepgsql_type_alter(domainoid); ++ + /* Setup new tuple */ + MemSet(new_record, (Datum) 0, sizeof(new_record)); + MemSet(new_record_nulls, false, sizeof(new_record_nulls)); +@@ -1711,6 +1749,8 @@ AlterDomainNotNull(List *names, bool notNull) + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); + ++ sepgsql_type_alter(domainoid); ++ + /* Is the domain already set to the desired constraint? */ + if (typTup->typnotnull == notNull) + { +@@ -1810,6 +1850,8 @@ AlterDomainDropConstraint(List *names, const char *constrName, + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); + ++ sepgsql_type_alter(domainoid); ++ + /* Grab an appropriate lock on the pg_constraint relation */ + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + +@@ -1884,6 +1926,8 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); + ++ sepgsql_type_alter(domainoid); ++ + if (!IsA(newConstraint, Constraint)) + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(newConstraint)); +@@ -2507,6 +2551,9 @@ RenameType(List *names, const char *newTypeName) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, + format_type_be(typeOid)); + ++ /* SELinux checks */ ++ sepgsql_type_alter_rename(typeOid, newTypeName); ++ + /* + * If it's a composite type, we need to check that it really is a + * free-standing composite type, and not a table's rowtype. We want people +@@ -2627,6 +2674,8 @@ AlterTypeOwner(List *names, Oid newOwnerId) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(typTup->typnamespace)); + } ++ /* SELinux checks */ ++ sepgsql_type_alter(HeapTupleGetOid(tup)); + + /* + * If it's a composite type, invoke ATExecChangeOwner so that we fix +@@ -2731,6 +2780,9 @@ AlterTypeNamespace(List *names, const char *newschema) + /* get schema OID and check its permissions */ + nspOid = LookupCreationNamespace(newschema); + ++ /* SELinux checks */ ++ sepgsql_type_alter_schema(typeOid, nspOid); ++ + /* don't allow direct alteration of array types */ + elemOid = get_element_type(typeOid); + if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid) +@@ -2882,3 +2934,100 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, + if (OidIsValid(arrayOid)) + AlterTypeNamespaceInternal(arrayOid, nspOid, true, true); + } ++ ++/* ++ * ALTER TYPE xxx SECURITY LABEL TO ... ++ */ ++void ++AlterTypeSecLabelInternal(Oid typeOid, Oid securityId) ++{ ++ Relation typeRel; ++ Form_pg_type typeForm; ++ HeapTuple tuple; ++ ++ typeRel = heap_open(TypeRelationId, RowExclusiveLock); ++ ++ tuple = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for type %u", typeOid); ++ typeForm = (Form_pg_type) GETSTRUCT(tuple); ++ ++ /* update it */ ++ HeapTupleSetSecid(tuple, securityId); ++ ++ simple_heap_update(typeRel, &tuple->t_self, tuple); ++ ++ CatalogUpdateIndexes(typeRel, tuple); ++ ++ /* if it is a composit type, update pg_class too */ ++ if (OidIsValid(typeForm->typrelid)) ++ { ++ Oid classSecId = seclabelMoveSecid(RelationRelationId, ++ TypeRelationId, ++ securityId); ++ AlterRelationSecLabelInternal(typeForm->typrelid, classSecId, 0); ++ } ++ ++ /* if it has an array type, update that too */ ++ if (OidIsValid(typeForm->typarray)) ++ AlterTypeSecLabelInternal(typeForm->typarray, securityId); ++ ++ heap_freetuple(tuple); ++ ++ heap_close(typeRel, RowExclusiveLock); ++} ++ ++void ++AlterTypeSecLabel(List *names, char *new_label) ++{ ++ Form_pg_type typeForm; ++ HeapTuple typtup; ++ TypeName *typename; ++ Oid typeOid; ++ Oid securityId; ++ ++ /* resolve type name */ ++ typename = makeTypeNameFromNameList(names); ++ typtup = LookupTypeName(NULL, typename, NULL); ++ if (!HeapTupleIsValid(typtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("type \"%s\" does not exist", ++ TypeNameToString(typename)))); ++ ++ typeForm = (Form_pg_type) GETSTRUCT(typtup); ++ typeOid = HeapTupleGetOid(typtup); ++ ++ /* ++ * If it's a composite type, we need to check that it really is a ++ * free-standing composite type, and not a table's rowtype. We want people ++ * to use ALTER TABLE not ALTER TYPE for that case. ++ */ ++ if (typeForm->typtype == TYPTYPE_COMPOSITE && ++ get_rel_relkind(typeForm->typrelid) != RELKIND_COMPOSITE_TYPE) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("%s is a table's row type", ++ format_type_be(typeOid)), ++ errhint("Use ALTER TABLE instead."))); ++ ++ /* don't allow direct alteration of array types, either */ ++ if (OidIsValid(typeForm->typelem) && ++ get_array_type(typeForm->typelem) == typeOid) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("cannot alter array type %s", ++ format_type_be(typeOid)))); ++ ++ /* DAC permission checks */ ++ if (!pg_type_ownercheck(typeOid, GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, ++ format_type_be(typeOid)); ++ ++ /* SELinux checks */ ++ securityId = sepgsql_type_relabel(typeOid, new_label); ++ ++ AlterTypeSecLabelInternal(typeOid, securityId); ++ ++ ReleaseSysCache(typtup); ++} +diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c +index 2f0788e..8de31ae 100644 +--- a/src/backend/commands/user.c ++++ b/src/backend/commands/user.c +@@ -21,11 +21,13 @@ + #include "catalog/pg_authid.h" + #include "catalog/pg_database.h" + #include "catalog/pg_db_role_setting.h" ++#include "catalog/pg_seclabel.h" + #include "commands/comment.h" + #include "commands/dbcommands.h" + #include "commands/user.h" + #include "libpq/md5.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "storage/lmgr.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -111,6 +113,7 @@ CreateRole(CreateRoleStmt *stmt) + DefElem *drolemembers = NULL; + DefElem *dadminmembers = NULL; + DefElem *dvalidUntil = NULL; ++ Oid securityId; + + /* The defaults can vary depending on the original statement type */ + switch (stmt->stmt_type) +@@ -279,6 +282,9 @@ CreateRole(CreateRoleStmt *stmt) + errmsg("permission denied to create role"))); + } + ++ /* SELinux checks */ ++ securityId = sepgsql_role_create(stmt->role); ++ + if (strcmp(stmt->role, "public") == 0 || + strcmp(stmt->role, "none") == 0) + ereport(ERROR, +@@ -365,6 +371,8 @@ CreateRole(CreateRoleStmt *stmt) + + tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls); + ++ HeapTupleSetSecid(tuple, securityId); ++ + /* + * Insert new record in the pg_authid table + */ +@@ -606,6 +614,8 @@ AlterRole(AlterRoleStmt *stmt) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied"))); + } ++ /* SELinux checks */ ++ sepgsql_role_alter(roleid); + + /* Convert validuntil to internal form */ + if (validUntil) +@@ -791,6 +801,8 @@ AlterRoleSet(AlterRoleSetStmt *stmt) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied"))); + } ++ /* SELinux checks */ ++ sepgsql_role_alter(HeapTupleGetOid(roletuple)); + + /* look up and lock the database, if specified */ + if (stmt->database != NULL) +@@ -886,6 +898,9 @@ DropRole(DropRoleStmt *stmt) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to drop superusers"))); + ++ /* SELinux checks */ ++ sepgsql_role_drop(roleid, false); ++ + /* + * Lock the role, so nobody can add dependencies to her while we drop + * her. We keep the lock until the end of transaction. +@@ -1282,6 +1297,9 @@ AddRoleMems(const char *rolename, Oid roleid, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to set grantor"))); + ++ /* SELinux checks */ ++ sepgsql_role_grant(roleid, true, memberIds); ++ + pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock); + pg_authmem_dsc = RelationGetDescr(pg_authmem_rel); + +@@ -1412,6 +1430,8 @@ DelRoleMems(const char *rolename, Oid roleid, + errmsg("must have admin option on role \"%s\"", + rolename))); + } ++ /* SELinux checks */ ++ sepgsql_role_grant(roleid, false, memberIds); + + pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock); + pg_authmem_dsc = RelationGetDescr(pg_authmem_rel); +diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c +index 49a206e..4646c41 100644 +--- a/src/backend/commands/vacuum.c ++++ b/src/backend/commands/vacuum.c +@@ -980,6 +980,12 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound, + relation_close(onerel, NoLock); + + /* ++ * VACUUM FULL also reclaim orphan security labels, if exist ++ */ ++ if (vacstmt->options & VACOPT_FULL) ++ seclabelRelationReclaim(relid); ++ ++ /* + * Complete the transaction and free all temporary memory used. + */ + PopActiveSnapshot(); +diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c +index d7a06bc..5480c83 100644 +--- a/src/backend/commands/view.c ++++ b/src/backend/commands/view.c +@@ -28,6 +28,7 @@ + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteManip.h" + #include "rewrite/rewriteSupport.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -167,6 +168,9 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + ++ /* SELinux checks */ ++ sepgsql_view_replace(viewOid); ++ + /* Also check it's not in use already */ + CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW"); + +diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c +index 5e555ad..963096d 100644 +--- a/src/backend/executor/execJunk.c ++++ b/src/backend/executor/execJunk.c +@@ -58,7 +58,8 @@ + * An optional resultSlot can be passed as well. + */ + JunkFilter * +-ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) ++ExecInitJunkFilter(List *targetList, bool hasoid, bool hassecid, ++ TupleTableSlot *slot) + { + JunkFilter *junkfilter; + TupleDesc cleanTupType; +@@ -70,7 +71,7 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) + /* + * Compute the tuple descriptor for the cleaned tuple. + */ +- cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); ++ cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hassecid); + + /* + * Use the given slot, or make a new slot if we weren't given one. +diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c +index d299310..febb02e 100644 +--- a/src/backend/executor/execMain.c ++++ b/src/backend/executor/execMain.c +@@ -38,6 +38,7 @@ + #include "access/xact.h" + #include "catalog/heap.h" + #include "catalog/namespace.h" ++#include "catalog/pg_seclabel.h" + #include "catalog/toasting.h" + #include "commands/tablespace.h" + #include "commands/trigger.h" +@@ -47,11 +48,13 @@ + #include "optimizer/clauses.h" + #include "parser/parse_clause.h" + #include "parser/parsetree.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/smgr.h" + #include "tcop/utility.h" + #include "utils/acl.h" ++#include "utils/guc.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" + #include "utils/snapmgr.h" +@@ -414,7 +417,16 @@ ExecCheckRTPerms(List *rangeTable) + + foreach(l, rangeTable) + { +- ExecCheckRTEPerms((RangeTblEntry *) lfirst(l)); ++ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); ++ ++ ExecCheckRTEPerms(rte); ++ ++ if (rte->rtekind == RTE_RELATION) ++ sepgsql_relation_perms(rte->relid, ++ rte->requiredPerms, ++ rte->selectedCols, ++ rte->modifiedCols, ++ true); + } + } + +@@ -829,6 +841,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) + + j = ExecInitJunkFilter(planstate->plan->targetlist, + tupType->tdhasoid, ++ tupType->tdhassecid, + ExecInitExtraTupleSlot(estate)); + estate->es_junkFilter = j; + +@@ -1065,6 +1078,37 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids) + return false; + } + ++/* ++ * ExecContextForcesSecids ++ * ++ * It is same with ExecContextForcesOids, except for it checks space ++ * for security id of the tuples. ++ */ ++bool ++ExecContextForcesSecids(PlanState *planstate, bool *hassecid) ++{ ++ ResultRelInfo *ri = planstate->state->es_result_relation_info; ++ ++ if (ri != NULL) ++ { ++ Relation rel = ri->ri_RelationDesc; ++ ++ if (rel != NULL) ++ { ++ *hassecid = RelationGetForm(rel)->relhassecids; ++ return true; ++ } ++ } ++ ++ if (planstate->state->es_select_into) ++ { ++ *hassecid = default_with_secids; ++ return true; ++ } ++ ++ return false; ++} ++ + /* ---------------------------------------------------------------- + * ExecEndPlan + * +@@ -2073,6 +2117,7 @@ OpenIntoRel(QueryDesc *queryDesc) + Oid intoRelationId; + TupleDesc tupdesc; + DR_intorel *myState; ++ Oid *secLabels; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; + + Assert(into); +@@ -2144,6 +2189,14 @@ OpenIntoRel(QueryDesc *queryDesc) + get_tablespace_name(tablespaceId)); + } + ++ /* SELinux checks */ ++ secLabels = sepgsql_relation_create(intoName, ++ RELKIND_RELATION, ++ queryDesc->tupDesc, ++ namespaceId, ++ NIL, ++ true); ++ + /* Parse and validate any reloptions */ + reloptions = transformRelOptions((Datum) 0, + into->options, +@@ -2174,7 +2227,8 @@ OpenIntoRel(QueryDesc *queryDesc) + into->onCommit, + reloptions, + true, +- allowSystemTableMods); ++ allowSystemTableMods, ++ secLabels); + + FreeTupleDesc(tupdesc); + +@@ -2298,6 +2352,11 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self) + */ + if (myState->rel->rd_rel->relhasoids) + HeapTupleSetOid(tuple, InvalidOid); ++ if (myState->rel->rd_rel->relhassecids) ++ HeapTupleSetSecid(tuple, InvalidOid); ++ ++ /* SELinux checks */ ++ sepgsql_tuple_insert(myState->rel, tuple); + + heap_insert(myState->rel, + tuple, +diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c +index 005e15e..6e93932 100644 +--- a/src/backend/executor/execQual.c ++++ b/src/backend/executor/execQual.c +@@ -49,6 +49,7 @@ + #include "optimizer/planner.h" + #include "parser/parse_coerce.h" + #include "pgstat.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -1177,6 +1178,9 @@ init_fcache(Oid foid, FuncExprState *fcache, + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); + ++ /* SELinux checks */ ++ sepgsql_proc_execute(foid); ++ + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if +@@ -1221,7 +1225,7 @@ init_fcache(Oid foid, FuncExprState *fcache, + else if (functypclass == TYPEFUNC_SCALAR) + { + /* Base data type, i.e. scalar */ +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, + (AttrNumber) 1, + NULL, +@@ -2111,7 +2115,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr, + /* + * Scalar type, so make a single-column descriptor + */ +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, + (AttrNumber) 1, + "column", +@@ -4108,6 +4112,9 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(acoerce->elemfuncid)); + ++ /* SELinux checks */ ++ sepgsql_proc_execute(acoerce->elemfuncid); ++ + /* Set up the primary fmgr lookup information */ + fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), + econtext->ecxt_per_query_memory); +diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c +index 53fe195..68f790f 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 "parser/parsetree.h" + #include "utils/memutils.h" + + +@@ -114,6 +115,9 @@ ExecScan(ScanState *node, + ProjectionInfo *projInfo; + ExprDoneCond isDone; + TupleTableSlot *resultSlot; ++ Scan *scan = (Scan *)node->ps.plan; ++ EState *estate = node->ps.state; ++ RangeTblEntry *rte = rt_fetch(scan->scanrelid, estate->es_range_table); + + /* + * Fetch data from node +@@ -125,7 +129,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 && !rte->rowlvPerms) + return ExecScanFetch(node, accessMtd, recheckMtd); + + /* +@@ -189,9 +193,20 @@ ExecScan(ScanState *node, + * when the qual is nil ... saves only a few cycles, but they add up + * ... + */ +- if (!qual || ExecQual(qual, econtext, false)) ++ if (sepgsql_rowlv_quals(node->ss_currentRelation, ++ rte->rowlvPerms, slot, false) && ++ (!qual || ExecQual(qual, econtext, false))) + { + /* ++ * NOTE: When FK checks, the Row-level features needs to raise ++ * an error after evaluation of all the given quals, to avoid ++ * incorrect error reports about unrelated tuples. ++ * We assume FK does not use malicious functions in the quals. ++ */ ++ sepgsql_rowlv_quals(node->ss_currentRelation, ++ rte->rowlvPerms, slot, true); ++ ++ /* + * Found a satisfactory scan tuple. + */ + if (projInfo) +@@ -258,6 +273,7 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc + int numattrs = tupdesc->natts; + int attrno; + bool hasoid; ++ bool hassecid; + ListCell *tlist_item = list_head(tlist); + + /* Check the tlist attributes */ +@@ -307,6 +323,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc + if (ExecContextForcesOids(ps, &hasoid) && + hasoid != tupdesc->tdhasoid) + return false; ++ if (ExecContextForcesSecids(ps, &hassecid) && ++ hassecid != tupdesc->tdhassecid) ++ return false; + + return true; + } +diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c +index 1caf976..64f6468 100644 +--- a/src/backend/executor/execTuples.c ++++ b/src/backend/executor/execTuples.c +@@ -98,7 +98,7 @@ + + + static TupleDesc ExecTypeFromTLInternal(List *targetList, +- bool hasoid, bool skipjunk); ++ bool hasoid, bool hassecid, bool skipjunk); + + + /* ---------------------------------------------------------------- +@@ -899,9 +899,9 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType) + * ---------------------------------------------------------------- + */ + TupleDesc +-ExecTypeFromTL(List *targetList, bool hasoid) ++ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid) + { +- return ExecTypeFromTLInternal(targetList, hasoid, false); ++ return ExecTypeFromTLInternal(targetList, hasoid, hassecid, false); + } + + /* ---------------------------------------------------------------- +@@ -911,13 +911,14 @@ ExecTypeFromTL(List *targetList, bool hasoid) + * ---------------------------------------------------------------- + */ + TupleDesc +-ExecCleanTypeFromTL(List *targetList, bool hasoid) ++ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid) + { +- return ExecTypeFromTLInternal(targetList, hasoid, true); ++ return ExecTypeFromTLInternal(targetList, hasoid, hassecid, true); + } + + static TupleDesc +-ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) ++ExecTypeFromTLInternal(List *targetList, ++ bool hasoid, bool hassecid, bool skipjunk) + { + TupleDesc typeInfo; + ListCell *l; +@@ -928,7 +929,7 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) + len = ExecCleanTargetListLength(targetList); + else + len = ExecTargetListLength(targetList); +- typeInfo = CreateTemplateTupleDesc(len, hasoid); ++ typeInfo = CreateTemplateTupleDesc(len, hasoid, hassecid); + + foreach(l, targetList) + { +@@ -960,7 +961,7 @@ ExecTypeFromExprList(List *exprList) + int cur_resno = 1; + char fldname[NAMEDATALEN]; + +- typeInfo = CreateTemplateTupleDesc(list_length(exprList), false); ++ typeInfo = CreateTemplateTupleDesc(list_length(exprList), false, false); + + foreach(l, exprList) + { +diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c +index 98e4a64..14f9404 100644 +--- a/src/backend/executor/execUtils.c ++++ b/src/backend/executor/execUtils.c +@@ -444,6 +444,7 @@ void + ExecAssignResultTypeFromTL(PlanState *planstate) + { + bool hasoid; ++ bool hassecid; + TupleDesc tupDesc; + + if (ExecContextForcesOids(planstate, &hasoid)) +@@ -456,12 +457,15 @@ ExecAssignResultTypeFromTL(PlanState *planstate) + hasoid = false; + } + ++ if (!ExecContextForcesSecids(planstate, &hassecid)) ++ hassecid = false; ++ + /* + * ExecTypeFromTL needs the parse-time representation of the tlist, not a + * list of ExprStates. This is good because some plan nodes don't bother + * to set up planstate->targetlist ... + */ +- tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid); ++ tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid, hassecid); + ExecAssignResultType(planstate, tupDesc); + } + +diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c +index d552698..ed1913f 100644 +--- a/src/backend/executor/functions.c ++++ b/src/backend/executor/functions.c +@@ -1153,7 +1153,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, + + /* Set up junk filter if needed */ + if (junkFilter) +- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); ++ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + } + else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) + { +@@ -1192,7 +1192,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, + } + /* Set up junk filter if needed */ + if (junkFilter) +- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); ++ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + return false; /* NOT returning whole tuple */ + } + } +@@ -1205,7 +1205,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, + * what the caller expects will happen at runtime. + */ + if (junkFilter) +- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); ++ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + return true; + } + Assert(tupdesc); +diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c +index ddd91fc..cfb685e 100644 +--- a/src/backend/executor/nodeAgg.c ++++ b/src/backend/executor/nodeAgg.c +@@ -89,6 +89,7 @@ + #include "optimizer/tlist.h" + #include "parser/parse_agg.h" + #include "parser/parse_coerce.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -1641,6 +1642,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) + get_func_name(finalfn_oid)); + } + } ++ /* SELinux checks */ ++ sepgsql_aggregate_execute(aggref->aggfnoid); + + /* resolve actual type of transition state, if polymorphic */ + aggtranstype = aggform->aggtranstype; +@@ -1722,7 +1725,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) + * Get a tupledesc corresponding to the inputs (including sort + * expressions) of the agg. + */ +- peraggstate->evaldesc = ExecTypeFromTL(aggref->args, false); ++ peraggstate->evaldesc = ExecTypeFromTL(aggref->args, false, false); + + /* Create slot we're going to do argument evaluation in */ + peraggstate->evalslot = ExecInitExtraTupleSlot(estate); +diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c +index 66e6b74..a31907c 100644 +--- a/src/backend/executor/nodeFunctionscan.c ++++ b/src/backend/executor/nodeFunctionscan.c +@@ -178,7 +178,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) + /* Base data type, i.e. scalar */ + char *attname = strVal(linitial(node->funccolnames)); + +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, + (AttrNumber) 1, + attname, +diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c +index ca6b0f8..b5c79d0 100644 +--- a/src/backend/executor/nodeMergejoin.c ++++ b/src/backend/executor/nodeMergejoin.c +@@ -98,6 +98,7 @@ + #include "executor/execdefs.h" + #include "executor/nodeMergejoin.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" +@@ -216,6 +217,9 @@ MJExamineQuals(List *mergeclauses, + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(cmpproc)); + ++ /* SELinux permissions */ ++ sepgsql_proc_execute(cmpproc); ++ + /* Set up the fmgr lookup information */ + fmgr_info(cmpproc, &(clause->cmpfinfo)); + +diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c +index 7856b66..eee2739 100644 +--- a/src/backend/executor/nodeModifyTable.c ++++ b/src/backend/executor/nodeModifyTable.c +@@ -38,11 +38,13 @@ + #include "postgres.h" + + #include "access/xact.h" ++#include "catalog/pg_seclabel.h" + #include "commands/trigger.h" + #include "executor/executor.h" + #include "executor/nodeModifyTable.h" + #include "miscadmin.h" + #include "nodes/nodeFuncs.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "utils/builtins.h" + #include "utils/memutils.h" +@@ -160,7 +162,8 @@ ExecProcessReturning(ProjectionInfo *projectReturning, + static TupleTableSlot * + ExecInsert(TupleTableSlot *slot, + TupleTableSlot *planSlot, +- EState *estate) ++ EState *estate, ++ Oid securityId) + { + HeapTuple tuple; + ResultRelInfo *resultRelInfo; +@@ -194,6 +197,8 @@ ExecInsert(TupleTableSlot *slot, + */ + if (resultRelationDesc->rd_rel->relhasoids) + HeapTupleSetOid(tuple, InvalidOid); ++ if (resultRelationDesc->rd_rel->relhassecids) ++ HeapTupleSetSecid(tuple, securityId); + + /* BEFORE ROW INSERT Triggers */ + if (resultRelInfo->ri_TrigDesc && +@@ -224,6 +229,8 @@ ExecInsert(TupleTableSlot *slot, + tuple = newtuple; + } + } ++ /* SELinux checks */ ++ sepgsql_tuple_insert(resultRelationDesc, tuple); + + /* + * Check the constraints of the tuple +@@ -421,7 +428,8 @@ ExecUpdate(ItemPointer tupleid, + TupleTableSlot *slot, + TupleTableSlot *planSlot, + EPQState *epqstate, +- EState *estate) ++ EState *estate, ++ Oid securityId) + { + HeapTuple tuple; + ResultRelInfo *resultRelInfo; +@@ -449,6 +457,13 @@ ExecUpdate(ItemPointer tupleid, + resultRelInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelInfo->ri_RelationDesc; + ++ /* ++ * If the result relation has writable system attributes, ++ * we store user given value (or InvalidOid) on the tuple. ++ */ ++ if (resultRelationDesc->rd_rel->relhassecids) ++ HeapTupleSetSecid(tuple, securityId); ++ + /* BEFORE ROW UPDATE Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) +@@ -479,6 +494,8 @@ ExecUpdate(ItemPointer tupleid, + tuple = newtuple; + } + } ++ /* SELinux checks */ ++ sepgsql_tuple_update(resultRelationDesc, tupleid, tuple); + + /* + * Check the constraints of the tuple +@@ -635,6 +652,42 @@ fireASTriggers(ModifyTableState *node) + } + } + ++/* ++ * FetchWritableSecLabel ++ * ++ * It moves user given security label into slot-> ++ */ ++static Oid ++FetchWritableSecLabel(Relation relation, ++ JunkFilter *junkfilter, ++ TupleTableSlot *slot) ++{ ++ Oid securityId = InvalidOid; ++ AttrNumber attno; ++ Datum datum; ++ bool isnull; ++ char *label; ++ ++ /* ++ * If no explicit label was given, set a default label later ++ */ ++ attno = ExecFindJunkAttribute(junkfilter, "security_label"); ++ if (attno != InvalidAttrNumber && !ignore_security_label_input) ++ { ++ datum = ExecGetJunkAttribute(slot, attno, &isnull); ++ if (isnull) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set NULL on \"security_label\""))); ++ ++ label = TextDatumGetCString(datum); ++ ++ securityId = seclabelTransInput(RelationGetRelid(relation), label); ++ ++ pfree(label); ++ } ++ return securityId; ++} + + /* ---------------------------------------------------------------- + * ExecModifyTable +@@ -682,6 +735,8 @@ ExecModifyTable(ModifyTableState *node) + */ + for (;;) + { ++ Oid securityId = InvalidOid; ++ + planSlot = ExecProcNode(subplanstate); + + if (TupIsNull(planSlot)) +@@ -705,6 +760,14 @@ ExecModifyTable(ModifyTableState *node) + + if (junkfilter != NULL) + { ++ Relation targetRel ++ = estate->es_result_relation_info->ri_RelationDesc; ++ ++ /* ++ * extract writable system column ++ */ ++ securityId = FetchWritableSecLabel(targetRel, junkfilter, slot); ++ + /* + * extract the 'ctid' junk attribute. + */ +@@ -734,11 +797,11 @@ ExecModifyTable(ModifyTableState *node) + switch (operation) + { + case CMD_INSERT: +- slot = ExecInsert(slot, planSlot, estate); ++ slot = ExecInsert(slot, planSlot, estate, securityId); + break; + case CMD_UPDATE: + slot = ExecUpdate(tupleid, slot, planSlot, +- &node->mt_epqstate, estate); ++ &node->mt_epqstate, estate, securityId); + break; + case CMD_DELETE: + slot = ExecDelete(tupleid, planSlot, +@@ -850,7 +913,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) + * RETURNING list. We assume the rest will look the same. + */ + tupDesc = ExecTypeFromTL((List *) linitial(node->returningLists), +- false); ++ false, false); + + /* Set up a slot for the output of the RETURNING projection(s) */ + ExecInitResultTupleSlot(estate, &mtstate->ps); +@@ -884,7 +947,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) + * We still must construct a dummy result tuple type, because InitPlan + * expects one (maybe should change that?). + */ +- tupDesc = ExecTypeFromTL(NIL, false); ++ tupDesc = ExecTypeFromTL(NIL, false, false); + ExecInitResultTupleSlot(estate, &mtstate->ps); + ExecAssignResultType(&mtstate->ps, tupDesc); + +@@ -976,6 +1039,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) + + j = ExecInitJunkFilter(subplan->targetlist, + resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, ++ resultRelInfo->ri_RelationDesc->rd_att->tdhassecid, + ExecInitExtraTupleSlot(estate)); + + if (operation == CMD_UPDATE || operation == CMD_DELETE) +diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c +index 9f1ff16..bef704b 100644 +--- a/src/backend/executor/nodeSubplan.c ++++ b/src/backend/executor/nodeSubplan.c +@@ -859,7 +859,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) + * (hack alert!). The righthand expressions will be evaluated in our + * own innerecontext. + */ +- tupDesc = ExecTypeFromTL(leftptlist, false); ++ tupDesc = ExecTypeFromTL(leftptlist, false, false); + slot = ExecInitExtraTupleSlot(estate); + ExecSetSlotDescriptor(slot, tupDesc); + sstate->projLeft = ExecBuildProjectionInfo(lefttlist, +@@ -867,7 +867,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) + slot, + NULL); + +- tupDesc = ExecTypeFromTL(rightptlist, false); ++ tupDesc = ExecTypeFromTL(rightptlist, false, false); + slot = ExecInitExtraTupleSlot(estate); + ExecSetSlotDescriptor(slot, tupDesc); + sstate->projRight = ExecBuildProjectionInfo(righttlist, +diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c +index 712bab1..e69f3c7 100644 +--- a/src/backend/executor/nodeWindowAgg.c ++++ b/src/backend/executor/nodeWindowAgg.c +@@ -43,6 +43,7 @@ + #include "optimizer/clauses.h" + #include "parser/parse_agg.h" + #include "parser/parse_coerce.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/datum.h" +@@ -1554,6 +1555,12 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(wfunc->winfnoid)); + ++ /* SELinux checks */ ++ if (wfunc->winagg) ++ sepgsql_aggregate_execute(wfunc->winfnoid); ++ else ++ sepgsql_proc_execute(wfunc->winfnoid); ++ + /* Fill in the perfuncstate data */ + perfuncstate->wfuncstate = wfuncstate; + perfuncstate->wfunc = wfunc; +diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c +index 7f0b5e4..2e2c63b 100644 +--- a/src/backend/executor/spi.c ++++ b/src/backend/executor/spi.c +@@ -767,6 +767,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, + mtuple->t_tableOid = tuple->t_tableOid; + if (rel->rd_att->tdhasoid) + HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); ++ if (rel->rd_att->tdhassecid) ++ HeapTupleSetSecid(mtuple, HeapTupleGetSecid(tuple)); + } + else + { +@@ -795,7 +797,8 @@ SPI_fnumber(TupleDesc tupdesc, const char *fname) + return res + 1; + } + +- sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ ); ++ /* "oid" and "security_label" will be accepted */ ++ sysatt = SystemAttributeByName(fname, true, true); + if (sysatt != NULL) + return sysatt->attnum; + +@@ -820,7 +823,7 @@ SPI_fname(TupleDesc tupdesc, int fnumber) + if (fnumber > 0) + att = tupdesc->attrs[fnumber - 1]; + else +- att = SystemAttributeDefinition(fnumber, true); ++ att = SystemAttributeDefinition(fnumber, true, true); + + return pstrdup(NameStr(att->attname)); + } +@@ -852,7 +855,7 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) + if (fnumber > 0) + typoid = tupdesc->attrs[fnumber - 1]->atttypid; + else +- typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; ++ typoid = (SystemAttributeDefinition(fnumber, true, true))->atttypid; + + getTypeOutputInfo(typoid, &foutoid, &typisvarlena); + +@@ -909,7 +912,7 @@ SPI_gettype(TupleDesc tupdesc, int fnumber) + if (fnumber > 0) + typoid = tupdesc->attrs[fnumber - 1]->atttypid; + else +- typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; ++ typoid = (SystemAttributeDefinition(fnumber, true, true))->atttypid; + + typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid)); + +@@ -939,7 +942,7 @@ SPI_gettypeid(TupleDesc tupdesc, int fnumber) + if (fnumber > 0) + return tupdesc->attrs[fnumber - 1]->atttypid; + else +- return (SystemAttributeDefinition(fnumber, true))->atttypid; ++ return (SystemAttributeDefinition(fnumber, true, true))->atttypid; + } + + char * +diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c +index f4f50f8..4ab5698 100644 +--- a/src/backend/libpq/be-fsstubs.c ++++ b/src/backend/libpq/be-fsstubs.c +@@ -46,6 +46,7 @@ + #include "libpq/be-fsstubs.h" + #include "libpq/libpq-fs.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "storage/large_object.h" + #include "utils/acl.h" +@@ -172,6 +173,8 @@ lo_read(int fd, char *buf, int len) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for large object %u", + cookies[fd]->id))); ++ /* SELinux checks */ ++ sepgsql_largeobject_read(cookies[fd]->id, cookies[fd]->snapshot); + + status = inv_read(cookies[fd], buf, len); + +@@ -204,6 +207,8 @@ lo_write(int fd, const char *buf, int len) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for large object %u", + cookies[fd]->id))); ++ /* SELinux checks */ ++ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot); + + status = inv_write(cookies[fd], buf, len); + +@@ -233,6 +238,7 @@ Datum + lo_creat(PG_FUNCTION_ARGS) + { + Oid lobjId; ++ Oid securityId; + + /* + * We don't actually need to store into fscxt, but create it anyway to +@@ -240,7 +246,10 @@ lo_creat(PG_FUNCTION_ARGS) + */ + CreateFSContext(); + +- lobjId = inv_create(InvalidOid); ++ /* SELinux checks */ ++ securityId = sepgsql_largeobject_create(InvalidOid); ++ ++ lobjId = inv_create(InvalidOid, securityId); + + PG_RETURN_OID(lobjId); + } +@@ -249,6 +258,7 @@ Datum + lo_create(PG_FUNCTION_ARGS) + { + Oid lobjId = PG_GETARG_OID(0); ++ Oid securityId; + + /* + * We don't actually need to store into fscxt, but create it anyway to +@@ -256,7 +266,10 @@ lo_create(PG_FUNCTION_ARGS) + */ + CreateFSContext(); + +- lobjId = inv_create(lobjId); ++ /* SELinux checks */ ++ securityId = sepgsql_largeobject_create(lobjId); ++ ++ lobjId = inv_create(lobjId, securityId); + + PG_RETURN_OID(lobjId); + } +@@ -286,6 +299,9 @@ lo_unlink(PG_FUNCTION_ARGS) + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be owner of large object %u", lobjId))); + ++ /* SELinux checks */ ++ sepgsql_largeobject_drop(lobjId, false); ++ + /* + * If there are any open LO FDs referencing that ID, close 'em. + */ +@@ -381,9 +397,10 @@ lo_import_internal(text *filename, Oid lobjOid) + int nbytes, + tmp; + char buf[BUFSIZE]; +- char fnamebuf[MAXPGPATH]; ++ char *fnamebuf = text_to_cstring(filename); + LargeObjectDesc *lobj; + Oid oid; ++ Oid securityId; + + #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS + if (!superuser()) +@@ -392,13 +409,14 @@ lo_import_internal(text *filename, Oid lobjOid) + errmsg("must be superuser to use server-side lo_import()"), + errhint("Anyone can use the client-side lo_import() provided by libpq."))); + #endif +- + CreateFSContext(); + ++ /* SELinux checks */ ++ securityId = sepgsql_largeobject_import(lobjOid, fnamebuf); ++ + /* + * open the file to be read in + */ +- text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); + fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666); + if (fd < 0) + ereport(ERROR, +@@ -409,7 +427,7 @@ lo_import_internal(text *filename, Oid lobjOid) + /* + * create an inversion object + */ +- oid = inv_create(lobjOid); ++ oid = inv_create(lobjOid, securityId); + + /* + * read in from the filesystem and write to the inversion object +@@ -447,7 +465,7 @@ lo_export(PG_FUNCTION_ARGS) + int nbytes, + tmp; + char buf[BUFSIZE]; +- char fnamebuf[MAXPGPATH]; ++ char *fnamebuf = text_to_cstring(filename); + LargeObjectDesc *lobj; + mode_t oumask; + +@@ -458,7 +476,6 @@ lo_export(PG_FUNCTION_ARGS) + errmsg("must be superuser to use server-side lo_export()"), + errhint("Anyone can use the client-side lo_export() provided by libpq."))); + #endif +- + CreateFSContext(); + + /* +@@ -466,6 +483,9 @@ lo_export(PG_FUNCTION_ARGS) + */ + lobj = inv_open(lobjId, INV_READ, fscxt); + ++ /* SELinux checks */ ++ sepgsql_largeobject_export(lobj->id, lobj->snapshot, fnamebuf); ++ + /* + * open the file to be written to + * +@@ -473,7 +493,6 @@ lo_export(PG_FUNCTION_ARGS) + * 022. This code used to drop it all the way to 0, but creating + * world-writable export files doesn't seem wise. + */ +- text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); + oumask = umask((mode_t) 0022); + fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666); + umask(oumask); +@@ -528,6 +547,9 @@ lo_truncate(PG_FUNCTION_ARGS) + errmsg("permission denied for large object %u", + cookies[fd]->id))); + ++ /* SELinux checks */ ++ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot); ++ + inv_truncate(cookies[fd], len); + + PG_RETURN_INT32(0); +diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c +index e770e89..9642a66 100644 +--- a/src/backend/nodes/copyfuncs.c ++++ b/src/backend/nodes/copyfuncs.c +@@ -1823,6 +1823,7 @@ _copyRangeTblEntry(RangeTblEntry *from) + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(modifiedCols); ++ COPY_SCALAR_FIELD(rowlvPerms); + + return newnode; + } +@@ -2755,6 +2756,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) + return newnode; + } + ++static AlterSecLabelStmt * ++_copyAlterSecLabelStmt(AlterSecLabelStmt *from) ++{ ++ AlterSecLabelStmt *newnode = makeNode(AlterSecLabelStmt); ++ ++ COPY_SCALAR_FIELD(objectType); ++ COPY_NODE_FIELD(relation); ++ COPY_NODE_FIELD(object); ++ COPY_NODE_FIELD(objarg); ++ COPY_STRING_FIELD(addname); ++ COPY_NODE_FIELD(secLabel); ++ ++ return newnode; ++} ++ + static RuleStmt * + _copyRuleStmt(RuleStmt *from) + { +@@ -3968,6 +3984,9 @@ copyObject(void *from) + case T_AlterOwnerStmt: + retval = _copyAlterOwnerStmt(from); + break; ++ case T_AlterSecLabelStmt: ++ retval = _copyAlterSecLabelStmt(from); ++ break; + case T_RuleStmt: + retval = _copyRuleStmt(from); + break; +diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c +index 5d83727..fd4071d 100644 +--- a/src/backend/nodes/equalfuncs.c ++++ b/src/backend/nodes/equalfuncs.c +@@ -1308,6 +1308,19 @@ _equalAlterOwnerStmt(AlterOwnerStmt *a, AlterOwnerStmt *b) + } + + static bool ++_equalAlterSecLabelStmt(AlterSecLabelStmt *a, AlterSecLabelStmt *b) ++{ ++ COMPARE_SCALAR_FIELD(objectType); ++ COMPARE_NODE_FIELD(relation); ++ COMPARE_NODE_FIELD(object); ++ COMPARE_NODE_FIELD(objarg); ++ COMPARE_STRING_FIELD(addname); ++ COMPARE_NODE_FIELD(secLabel); ++ ++ return true; ++} ++ ++static bool + _equalRuleStmt(RuleStmt *a, RuleStmt *b) + { + COMPARE_NODE_FIELD(relation); +@@ -2186,6 +2199,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) + COMPARE_SCALAR_FIELD(checkAsUser); + COMPARE_BITMAPSET_FIELD(selectedCols); + COMPARE_BITMAPSET_FIELD(modifiedCols); ++ COMPARE_SCALAR_FIELD(rowlvPerms); + + return true; + } +@@ -2657,6 +2671,9 @@ equal(void *a, void *b) + case T_AlterOwnerStmt: + retval = _equalAlterOwnerStmt(a, b); + break; ++ case T_AlterSecLabelStmt: ++ retval = _equalAlterSecLabelStmt(a, b); ++ break; + case T_RuleStmt: + retval = _equalRuleStmt(a, b); + break; +diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c +index e7dae4b..4e8b350 100644 +--- a/src/backend/nodes/outfuncs.c ++++ b/src/backend/nodes/outfuncs.c +@@ -2135,6 +2135,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) + WRITE_OID_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(modifiedCols); ++ WRITE_UINT_FIELD(rowlvPerms); + } + + static void +diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c +index bc6e2a6..792b9ff 100644 +--- a/src/backend/nodes/readfuncs.c ++++ b/src/backend/nodes/readfuncs.c +@@ -1171,6 +1171,7 @@ _readRangeTblEntry(void) + READ_OID_FIELD(checkAsUser); + READ_BITMAPSET_FIELD(selectedCols); + READ_BITMAPSET_FIELD(modifiedCols); ++ READ_UINT_FIELD(rowlvPerms); + + READ_DONE(); + } +diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c +index 3950ab4..18e5d80 100644 +--- a/src/backend/optimizer/plan/planner.c ++++ b/src/backend/optimizer/plan/planner.c +@@ -39,6 +39,7 @@ + #include "parser/parse_expr.h" + #include "parser/parse_oper.h" + #include "parser/parsetree.h" ++#include "sepgsql/hooks.h" + #include "utils/lsyscache.h" + #include "utils/syscache.h" + +@@ -371,6 +372,16 @@ subquery_planner(PlannerGlobal *glob, Query *parse, + } + + /* ++ * We set up rowlvPerms before expanding inherited tables. ++ */ ++ foreach(l, parse->rtable) ++ { ++ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); ++ ++ rte->rowlvPerms = sepgsql_rowlv_permissions(rte); ++ } ++ ++ /* + * Preprocess RowMark information. We need to do this after subquery + * pullup (so that all non-inherited RTEs are present) and before + * inheritance expansion (so that the info is available for +diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c +index e525ba6..6474fbc 100644 +--- a/src/backend/optimizer/util/clauses.c ++++ b/src/backend/optimizer/util/clauses.c +@@ -38,6 +38,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_func.h" + #include "rewrite/rewriteManip.h" ++#include "sepgsql/hooks.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -3712,6 +3713,10 @@ inline_function(Oid funcid, Oid result_type, List *args, + if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) + return NULL; + ++ /* SELinux checks */ ++ if (!sepgsql_proc_be_inlined(func_tuple)) ++ return NULL; ++ + /* + * Make a temporary memory context, so that we don't leak all the stuff + * that parsing might create. +@@ -4164,7 +4169,8 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) + funcform->provolatile == PROVOLATILE_VOLATILE || + funcform->prosecdef || + !funcform->proretset || +- !heap_attisnull(func_tuple, Anum_pg_proc_proconfig)) ++ !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || ++ !sepgsql_proc_be_inlined(func_tuple)) + { + ReleaseSysCache(func_tuple); + return NULL; +diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c +index 6b99a10..4eb9d24 100644 +--- a/src/backend/parser/analyze.c ++++ b/src/backend/parser/analyze.c +@@ -25,6 +25,7 @@ + #include "postgres.h" + + #include "access/sysattr.h" ++#include "catalog/heap.h" + #include "catalog/pg_type.h" + #include "nodes/makefuncs.h" + #include "nodes/nodeFuncs.h" +@@ -40,6 +41,7 @@ + #include "parser/parse_target.h" + #include "parser/parsetree.h" + #include "rewrite/rewriteManip.h" ++#include "utils/guc.h" + #include "utils/rel.h" + + +@@ -651,7 +653,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) + tle = makeTargetEntry(expr, + attr_num, + col->name, +- false); ++ attr_num < 0 ? true : false); + qry->targetList = lappend(qry->targetList, tle); + + rte->modifiedCols = bms_add_member(rte->modifiedCols, +diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y +index b793c4d..866e0ba 100644 +--- a/src/backend/parser/gram.y ++++ b/src/backend/parser/gram.y +@@ -183,8 +183,8 @@ static TypeName *TableFuncTypeName(List *columns); + %type stmt schema_stmt + AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt + AlterForeignServerStmt AlterGroupStmt +- AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt +- AlterUserStmt AlterUserMappingStmt AlterUserSetStmt ++ AlterObjectSchemaStmt AlterOwnerStmt AlterSecLabelStmt AlterSeqStmt ++ AlterTableStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt + AlterRoleStmt AlterRoleSetStmt + AlterDefaultPrivilegesStmt DefACLAction + AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt +@@ -422,6 +422,8 @@ static TypeName *TableFuncTypeName(List *columns); + %type OptTableSpace OptConsTableSpace OptTableSpaceOwner + %type opt_check_option + ++%type SecLabelItem ++ + %type xml_attribute_el + %type xml_attribute_list xml_attributes + %type xml_root_version opt_xml_root_standalone +@@ -498,7 +500,7 @@ static TypeName *TableFuncTypeName(List *columns); + + KEY + +- LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING ++ LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING + LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP + LOCATION LOCK_P LOGIN_P + +@@ -654,6 +656,7 @@ stmt : + | AlterGroupStmt + | AlterObjectSchemaStmt + | AlterOwnerStmt ++ | AlterSecLabelStmt + | AlterSeqStmt + | AlterTableStmt + | AlterRoleSetStmt +@@ -1758,6 +1761,20 @@ alter_table_cmd: + n->subtype = AT_DropOids; + $$ = (Node *)n; + } ++ /* ALTER TABLE SET WITH SECURITY LABEL */ ++ | SET WITH SECURITY LABEL ++ { ++ AlterTableCmd *n = makeNode(AlterTableCmd); ++ n->subtype = AT_AddSecLabel; ++ $$ = (Node *)n; ++ } ++ /* ALTER TABLE SET WITHOUT SECURITY LABEL */ ++ | SET WITHOUT SECURITY LABEL ++ { ++ AlterTableCmd *n = makeNode(AlterTableCmd); ++ n->subtype = AT_DropSecLabel; ++ $$ = (Node *)n; ++ } + /* ALTER TABLE CLUSTER ON */ + | CLUSTER ON name + { +@@ -6022,6 +6039,102 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId + } + ; + ++/***************************************************************************** ++ * ++ * ALTER THING name SECURITY LABEL TO new_label ++ * ++ *****************************************************************************/ ++ ++AlterSecLabelStmt: ALTER DATABASE database_name SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_DATABASE; ++ n->object = list_make1(makeString($3)); ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ | ALTER SCHEMA name SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_SCHEMA; ++ n->object = list_make1(makeString($3)); ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ | ALTER TABLE relation_expr SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_TABLE; ++ n->relation = $3; ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ | ALTER TABLE relation_expr ALTER opt_column ColId SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_COLUMN; ++ n->relation = $3; ++ n->addname = $6; ++ n->secLabel = $7; ++ $$ = (Node *)n; ++ } ++ | ALTER SEQUENCE qualified_name SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_SEQUENCE; ++ n->relation = $3; ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ | ALTER VIEW qualified_name SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_VIEW; ++ n->relation = $3; ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ | ALTER FUNCTION function_with_argtypes SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_FUNCTION; ++ n->object = $3->funcname; ++ n->objarg = $3->funcargs; ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ | ALTER AGGREGATE func_name aggr_args SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_AGGREGATE; ++ n->object = $3; ++ n->objarg = $4; ++ n->secLabel = $5; ++ $$ = (Node *)n; ++ } ++ | ALTER LARGE_P OBJECT_P Iconst SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_LARGEOBJECT; ++ n->object = list_make1(makeInteger($4)); ++ n->secLabel = $5; ++ $$ = (Node *)n; ++ } ++ | ALTER TYPE_P any_name SecLabelItem ++ { ++ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); ++ n->objectType = OBJECT_TYPE; ++ n->object = $3; ++ n->secLabel = $4; ++ $$ = (Node *)n; ++ } ++ ; ++ ++SecLabelItem: SECURITY LABEL TO Sconst ++ { ++ $$ = makeString($4); ++ } ++ ; + + /***************************************************************************** + * +@@ -10921,6 +11034,7 @@ unreserved_keyword: + | INVOKER + | ISOLATION + | KEY ++ | LABEL + | LANGUAGE + | LARGE_P + | LAST_P +diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c +index 38c7e91..fbc4e9f 100644 +--- a/src/backend/parser/parse_relation.c ++++ b/src/backend/parser/parse_relation.c +@@ -2302,8 +2302,8 @@ specialAttNum(const char *attname) + { + Form_pg_attribute sysatt; + +- sysatt = SystemAttributeByName(attname, +- true /* "oid" will be accepted */ ); ++ /* "oid" and "security_label" will be accepted */ ++ sysatt = SystemAttributeByName(attname, true, true); + if (sysatt != NULL) + return sysatt->attnum; + return InvalidAttrNumber; +@@ -2324,7 +2324,9 @@ attnumAttName(Relation rd, int attid) + { + Form_pg_attribute sysatt; + +- sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids); ++ sysatt = SystemAttributeDefinition(attid, ++ rd->rd_rel->relhasoids, ++ rd->rd_rel->relhassecids); + return &sysatt->attname; + } + if (attid > rd->rd_att->natts) +@@ -2346,7 +2348,9 @@ attnumTypeId(Relation rd, int attid) + { + Form_pg_attribute sysatt; + +- sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids); ++ sysatt = SystemAttributeDefinition(attid, ++ rd->rd_rel->relhasoids, ++ rd->rd_rel->relhassecids); + return sysatt->atttypid; + } + if (attid > rd->rd_att->natts) +diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c +index e542dc0..8191f94 100644 +--- a/src/backend/parser/parse_target.c ++++ b/src/backend/parser/parse_target.c +@@ -14,6 +14,7 @@ + */ + #include "postgres.h" + ++#include "catalog/heap.h" + #include "catalog/pg_type.h" + #include "commands/dbcommands.h" + #include "funcapi.h" +@@ -365,16 +366,34 @@ transformAssignedExpr(ParseState *pstate, + Oid attrtype; /* type of target column */ + int32 attrtypmod; + Relation rd = pstate->p_target_relation; ++ bool relhasoids = RelationGetForm(rd)->relhasoids; ++ bool relhassecids = RelationGetForm(rd)->relhassecids; + + Assert(rd != NULL); +- if (attrno <= 0) +- ereport(ERROR, +- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), +- errmsg("cannot assign to system column \"%s\"", +- colname), +- parser_errposition(pstate, location))); +- attrtype = attnumTypeId(rd, attrno); +- attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ++ if (attrno > 0) ++ { ++ attrtype = attnumTypeId(rd, attrno); ++ attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ++ } ++ else ++ { ++ Form_pg_attribute attr; ++ ++ attr = SystemAttributeDefinition(attrno, relhasoids, relhassecids); ++ if (attr && SystemAttributeWritable(attrno, relhasoids, relhassecids)) ++ { ++ attrtype = attr->atttypid; ++ attrtypmod = attr->atttypmod; ++ } ++ else ++ { ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("cannot assign to system column \"%s\"", colname), ++ parser_errposition(pstate, location))); ++ return NULL; ++ } ++ } + + /* + * If the expression is a DEFAULT placeholder, insert the attribute's +@@ -503,6 +522,10 @@ updateTargetListEntry(ParseState *pstate, + List *indirection, + int location) + { ++ Relation rel = pstate->p_target_relation; ++ bool relhasoids = RelationGetForm(rel)->relhasoids; ++ bool relhassecids = RelationGetForm(rel)->relhassecids; ++ + /* Fix up expression as needed */ + tle->expr = transformAssignedExpr(pstate, + tle->expr, +@@ -519,6 +542,9 @@ updateTargetListEntry(ParseState *pstate, + */ + tle->resno = (AttrNumber) attrno; + tle->resname = colname; ++ ++ if (SystemAttributeWritable(attrno, relhasoids, relhassecids)) ++ tle->resjunk = true; + } + + +@@ -793,6 +819,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) + Bitmapset *wholecols = NULL; + Bitmapset *partialcols = NULL; + ListCell *tl; ++ uint32 system_attrs = 0UL; + + foreach(tl, cols) + { +@@ -801,14 +828,42 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) + int attrno; + + /* Lookup column name, ereport on failure */ +- attrno = attnameAttNum(pstate->p_target_relation, name, false); ++ attrno = attnameAttNum(pstate->p_target_relation, name, true); + if (attrno == InvalidAttrNumber) ++ { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + name, + RelationGetRelationName(pstate->p_target_relation)), + parser_errposition(pstate, col->location))); ++ } ++ else if (attrno < 0) ++ { ++ Relation rel = pstate->p_target_relation; ++ bool relhasoids = RelationGetForm(rel)->relhasoids; ++ bool relhassecids = RelationGetForm(rel)->relhassecids; ++ ++ if (SystemAttributeWritable(attrno, relhasoids, relhassecids)) ++ { ++ uint32 mask = (1<<(-attrno)); ++ ++ if ((system_attrs & mask) != 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_DUPLICATE_COLUMN), ++ errmsg("column \"%s\" specified more than once", ++ name), ++ parser_errposition(pstate, col->location))); ++ system_attrs |= mask; ++ *attrnos = lappend_int(*attrnos, attrno); ++ continue; ++ } ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), ++ errmsg("column \"%s\" of relation \"%s\" is system column", ++ name, RelationGetRelationName(rel)), ++ parser_errposition(pstate, col->location))); ++ } + + /* + * Check for duplicates, but only of whole columns --- we allow +@@ -1263,7 +1318,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) + expandRTE(rte, var->varno, 0, var->location, false, + &names, &vars); + +- tupleDesc = CreateTemplateTupleDesc(list_length(vars), false); ++ tupleDesc = CreateTemplateTupleDesc(list_length(vars), false, false); + i = 1; + forboth(lname, names, lvar, vars) + { +diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c +index 90d5c76..b78f9ba 100644 +--- a/src/backend/parser/parse_utilcmd.c ++++ b/src/backend/parser/parse_utilcmd.c +@@ -53,8 +53,10 @@ + #include "parser/parse_utilcmd.h" + #include "parser/parser.h" + #include "rewrite/rewriteManip.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" ++#include "utils/guc.h" + #include "utils/lsyscache.h" + #include "utils/relcache.h" + #include "utils/syscache.h" +@@ -70,6 +72,7 @@ typedef struct + List *inhRelations; /* relations to inherit from */ + bool isalter; /* true if altering existing table */ + bool hasoids; /* does relation have an OID column? */ ++ bool hassecids; /* does relation have an security label? */ + List *columns; /* ColumnDef items */ + List *ckconstraints; /* CHECK constraints */ + List *fkconstraints; /* FOREIGN KEY constraints */ +@@ -185,6 +188,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) + cxt.alist = NIL; + cxt.pkey = NULL; + cxt.hasoids = interpretOidsOption(stmt->options); ++ cxt.hassecids = default_with_secids; + + Assert(!stmt->ofTypename || !stmt->inhRelations); /* grammar enforces */ + +@@ -587,6 +591,9 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(relation)); + ++ /* SELinux checks */ ++ sepgsql_relation_getattr(RelationGetRelid(relation)); ++ + tupleDesc = RelationGetDescr(relation); + constr = tupleDesc->constr; + +@@ -1368,7 +1375,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) + if (constraint->contype == CONSTR_PRIMARY) + column->is_not_null = TRUE; + } +- else if (SystemAttributeByName(key, cxt->hasoids) != NULL) ++ else if (SystemAttributeByName(key, cxt->hasoids, cxt->hassecids) != NULL) + { + /* + * column will be a system column in the new table, so accept it. +@@ -1945,6 +1952,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) + cxt.inhRelations = NIL; + cxt.isalter = true; + cxt.hasoids = false; /* need not be right */ ++ cxt.hassecids = false; /* need not be right */ + cxt.columns = NIL; + cxt.ckconstraints = NIL; + cxt.fkconstraints = NIL; +diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c +index aa8f2da..604a72c 100644 +--- a/src/backend/postmaster/postmaster.c ++++ b/src/backend/postmaster/postmaster.c +@@ -109,6 +109,7 @@ + #include "postmaster/postmaster.h" + #include "postmaster/syslogger.h" + #include "replication/walsender.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "storage/ipc.h" + #include "storage/pg_shmem.h" +@@ -212,7 +213,8 @@ static pid_t StartupPID = 0, + AutoVacPID = 0, + PgArchPID = 0, + PgStatPID = 0, +- SysLoggerPID = 0; ++ SysLoggerPID = 0, ++ SecWorkerPID = 0; + + /* Startup/shutdown state */ + #define NoShutdown 0 +@@ -467,6 +469,7 @@ static void ShmemBackendArrayRemove(Backend *bn); + #define StartBackgroundWriter() StartChildProcess(BgWriterProcess) + #define StartWalWriter() StartChildProcess(WalWriterProcess) + #define StartWalReceiver() StartChildProcess(WalReceiverProcess) ++#define StartSecurityWorker() StartChildProcess(SecurityWorkerProcess) + + /* Macros to check exit status of a child process */ + #define EXIT_STATUS_0(st) ((st) == 0) +@@ -1480,6 +1483,11 @@ ServerLoop(void) + if (PgStatPID == 0 && pmState == PM_RUN) + PgStatPID = pgstat_start(); + ++ /* If we have lost security worker, try to start a new one */ ++ if (SecWorkerPID == 0 && pmState == PM_RUN && ++ sepgsql_worker_needed()) ++ SecWorkerPID = StartSecurityWorker(); ++ + /* If we need to signal the autovacuum launcher, do so now */ + if (avlauncher_needs_signal) + { +@@ -2120,6 +2128,8 @@ SIGHUP_handler(SIGNAL_ARGS) + signal_child(SysLoggerPID, SIGHUP); + if (PgStatPID != 0) + signal_child(PgStatPID, SIGHUP); ++ if (SecWorkerPID != 0) ++ signal_child(SecWorkerPID, SIGHUP); + + /* Reload authentication config files too */ + if (!load_hba()) +@@ -2180,6 +2190,10 @@ pmdie(SIGNAL_ARGS) + /* and the walwriter too */ + if (WalWriterPID != 0) + signal_child(WalWriterPID, SIGTERM); ++ /* and the security worker too */ ++ if (SecWorkerPID != 0) ++ signal_child(SecWorkerPID, SIGTERM); ++ + /* + * If we're in recovery, we can't kill the startup process + * right away, because at present doing so does not release +@@ -2241,6 +2255,9 @@ pmdie(SIGNAL_ARGS) + /* and the walwriter too */ + if (WalWriterPID != 0) + signal_child(WalWriterPID, SIGTERM); ++ /* and the security worker too */ ++ if (SecWorkerPID != 0) ++ signal_child(SecWorkerPID, SIGTERM); + pmState = PM_WAIT_BACKENDS; + } + +@@ -2276,6 +2293,8 @@ pmdie(SIGNAL_ARGS) + signal_child(PgArchPID, SIGQUIT); + if (PgStatPID != 0) + signal_child(PgStatPID, SIGQUIT); ++ if (SecWorkerPID != 0) ++ signal_child(SecWorkerPID, SIGQUIT); + ExitPostmaster(0); + break; + } +@@ -2547,6 +2566,16 @@ reaper(SIGNAL_ARGS) + continue; + } + ++ /* Was it the security worker process? */ ++ if (pid == SecWorkerPID) ++ { ++ SecWorkerPID = 0; ++ if (!EXIT_STATUS_0(exitstatus)) ++ LogChildExit(LOG, _("security worker process"), ++ pid, exitstatus); ++ continue; ++ } ++ + /* + * Else do standard backend child cleanup. + */ +@@ -2750,6 +2779,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) + signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); + } + ++ /* Take care of the security worker process too */ ++ if (pid == SecWorkerPID) ++ SecWorkerPID = 0; ++ else if (SecWorkerPID != 0 && !FatalError) ++ { ++ ereport(DEBUG2, ++ (errmsg_internal("sending %s to process %d", ++ (SendStop ? "SIGSTOP" : "SIGQUIT"), ++ (int) SecWorkerPID))); ++ signal_child(SecWorkerPID, (SendStop ? SIGSTOP : SIGQUIT)); ++ } ++ + /* + * Force a power-cycle of the pgarch process too. (This isn't absolutely + * necessary, but it seems like a good idea for robustness, and it +@@ -2906,7 +2947,8 @@ PostmasterStateMachine(void) + WalReceiverPID == 0 && + (BgWriterPID == 0 || !FatalError) && + WalWriterPID == 0 && +- AutoVacPID == 0) ++ AutoVacPID == 0 && ++ SecWorkerPID == 0) + { + if (FatalError) + { +diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c +index 68be146..6ba1d2f 100644 +--- a/src/backend/rewrite/rewriteDefine.c ++++ b/src/backend/rewrite/rewriteDefine.c +@@ -27,6 +27,7 @@ + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteManip.h" + #include "rewrite/rewriteSupport.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/inval.h" +@@ -265,6 +266,9 @@ DefineQueryRewrite(char *rulename, + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(event_relation)); + ++ /* SELinux checks */ ++ sepgsql_rule_create(event_relid, rulename); ++ + /* + * No rule actions that modify OLD or NEW + */ +diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c +index c1c5ce9..0f6a446 100644 +--- a/src/backend/rewrite/rewriteRemove.c ++++ b/src/backend/rewrite/rewriteRemove.c +@@ -22,6 +22,7 @@ + #include "catalog/pg_rewrite.h" + #include "miscadmin.h" + #include "rewrite/rewriteRemove.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/fmgroids.h" + #include "utils/inval.h" +@@ -77,6 +78,9 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(eventRelationOid)); + ++ /* SELinux checks */ ++ sepgsql_rule_drop(eventRelationOid, ruleName, false); ++ + /* + * Do the deletion + */ +diff --git a/src/backend/sepgsql/Makefile b/src/backend/sepgsql/Makefile +new file mode 100644 +index 0000000..c8fb740 +--- /dev/null ++++ b/src/backend/sepgsql/Makefile +@@ -0,0 +1,21 @@ ++# ++# Makefile for security subsystem ++# ++ ++subdir = src/backend/sepgsql ++top_builddir = ../../.. ++include $(top_builddir)/src/Makefile.global ++ ++ifeq ($(enable_selinux), yes) ++OBJS = selinux.o avc.o label.o ++else ++OBJS = dummy.o ++endif ++ ++OBJS += rowlv.o ++ ++OBJS += misc.o database.o schema.o relation.o attribute.o proc.o \ ++ type.o tablespace.o operator.o role.o blob.o conversion.o \ ++ tsearch.o fdw.o file.o ++ ++include $(top_srcdir)/src/backend/common.mk +diff --git a/src/backend/sepgsql/attribute.c b/src/backend/sepgsql/attribute.c +new file mode 100644 +index 0000000..3b9c916 +--- /dev/null ++++ b/src/backend/sepgsql/attribute.c +@@ -0,0 +1,260 @@ ++/* ++ * attribute.c ++ * ++ * SELinux hooks related to attribute ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/heapam.h" ++#include "access/sysattr.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_class.h" ++#include "catalog/pg_seclabel.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_attribute_common(Oid relOid, AttrNumber attno, ++ uint32 required, bool abort) ++{ ++ Form_pg_attribute attForm; ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ char auname[NAMEDATALEN * 2 + 10]; ++ bool retval; ++ ++ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); ++ ++ tuple = SearchSysCache2(ATTNUM, ++ ObjectIdGetDatum(relOid), ++ Int16GetDatum(attno)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for attribute %d of relation %u", ++ attno, relOid); ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ ++ tsid.relid = AttributeRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ snprintf(auname, sizeof(auname), "%s.%s", ++ get_rel_name(relOid), NameStr(attForm->attname)); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_COLUMN, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_attribute_create(Oid relOid, const char *attName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char auname[NAMEDATALEN * 2 + 10]; ++ ++ if (get_rel_relkind(relOid) != RELKIND_RELATION) ++ { ++ sepgsql_relation_common(relOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ nsid.relid = RelationRelationId; ++ nsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); ++ ++ nsid = sepgsql_move_secid(AttributeRelationId, nsid); ++ ++ return nsid.secid; ++ } ++ ++ nsid = sepgsql_get_default_column_secid(relOid); ++ snprintf(auname, sizeof(auname), "%s.%s", ++ get_rel_name(relOid), attName); ++ ++ /* db_column:{create} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_COLUMN, ++ SEPG_DB_COLUMN__CREATE, ++ auname, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_attribute_alter(Oid relOid, const char *attName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ AttrNumber attnum = get_attnum(relOid, attName); ++ ++ if (attnum == InvalidAttrNumber) ++ return; /* to be failed later */ ++ ++ if (get_rel_relkind(relOid) == RELKIND_RELATION) ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__SETATTR, true); ++ else ++ sepgsql_relation_common(relOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_attribute_relabel(Oid relOid, const char *attName, char *new_label) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ AttrNumber attnum = get_attnum(relOid, attName); ++ sepgsql_sid_t nsid; ++ char auname[NAMEDATALEN * 2 + 10]; ++ ++ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); ++ if (attnum == InvalidAttrNumber) ++ return InvalidOid; /* to be failed later */ ++ ++ nsid.relid = AttributeRelationId; ++ nsid.secid = seclabelTransInput(nsid.relid, new_label); ++ ++ snprintf(auname, sizeof(auname), "%s.%s", ++ get_rel_name(relOid), get_attname(relOid, attnum)); ++ ++ /* db_column:{setattr relabelfrom} */ ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__SETATTR | ++ SEPG_DB_COLUMN__RELABELFROM, ++ true); ++ ++ /* db_column:{relabelto} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_COLUMN, ++ SEPG_DB_COLUMN__RELABELTO, ++ auname, ++ true); ++ return nsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_attribute_drop(Oid relOid, const char *attName, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ AttrNumber attnum = get_attnum(relOid, attName); ++ ++ /* ++ * If not found, the caller raises an error with an appropriate ++ * error message. ++ */ ++ if (attnum == InvalidAttrNumber) ++ return; ++ ++ if (get_rel_relkind(relOid) == RELKIND_RELATION) ++ { ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__DROP, true); ++ /* ++ * ALTER TABLE SET WITHOUT SECURITY LABEL is equivalent to ++ * relabel all the tuples within the target relation. ++ * In this case, we need to check {relabelfrom relabelto} ++ */ ++ if (!cascade && ++ attnum == SecurityLabelAttributeNumber) ++ { ++ Relation rel; ++ HeapScanDesc scan; ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ ++ rel = heap_open(relOid, AccessShareLock); ++ ++ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); ++ ++ while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) ++ { ++ /* db_tuple:{update relabelfrom} */ ++ tsid.relid = relOid; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__UPDATE | ++ SEPG_DB_TUPLE__RELABELFROM, ++ NULL, ++ true); ++ } ++ heap_endscan(scan); ++ ++ heap_close(rel, AccessShareLock); ++ ++ /* db_tuple:{relabelto} */ ++ tsid.relid = RelationRelationId; ++ tsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); ++ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELTO, ++ NULL, ++ true); ++ } ++ } ++ else if (!cascade) ++ sepgsql_relation_common(relOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_attribute_grant(Oid relOid, AttrNumber attnum) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ if (get_rel_relkind(relOid) == RELKIND_RELATION) ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__SETATTR, true); ++ else ++ sepgsql_relation_common(relOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_attribute_comment(Oid relOid, AttrNumber attnum) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ if (get_rel_relkind(relOid) == RELKIND_RELATION) ++ sepgsql_attribute_common(relOid, attnum, ++ SEPG_DB_COLUMN__SETATTR, true); ++ else ++ sepgsql_relation_common(relOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/avc.c b/src/backend/sepgsql/avc.c +new file mode 100644 +index 0000000..5f58974 +--- /dev/null ++++ b/src/backend/sepgsql/avc.c +@@ -0,0 +1,503 @@ ++/* ++ * avc.c ++ * userspace access vector cache ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/hash.h" ++#include "access/xact.h" ++#include "catalog/pg_seclabel.h" ++#include "libpq/libpq-be.h" ++#include "libpq/pqsignal.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "storage/shmem.h" ++#include "storage/lock.h" ++#include "utils/memutils.h" ++ ++#include ++#include ++#include ++ ++/* ------------------------------------------------------------ ++ * ++ * Userspace Access Vector Cache ++ * ++ * ------------------------------------------------------------ ++ */ ++static MemoryContext AvcMemCtx = NULL; ++ ++#define AVC_HASH_NUM_SLOTS 256 ++#define AVC_HASH_NUM_NODES 180 ++ ++#define avc_hash_key(trelid, tsecid, tclass, nrelid) \ ++ (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3) ^ (nrelid))) ++ ++typedef struct _avc_datum ++{ ++ uint32 hash_key; ++ ++ uint16 tclass; ++ sepgsql_sid_t tsid; ++ sepgsql_sid_t nsid; ++ char *tcontext; ++ char *ncontext; ++ ++ uint32 allowed; ++ uint32 auditallow; ++ uint32 auditdeny; ++ bool permissive; ++ ++ bool hot_cache; ++} avc_datum; ++ ++typedef struct _avc_page ++{ ++ struct _avc_page *next; ++ ++ List *slot[AVC_HASH_NUM_SLOTS]; ++ ++ uint32 avc_count; ++ uint32 lru_hint; ++ ++ char scontext[1]; ++} avc_page; ++ ++static avc_page *current_page = NULL; ++ ++static int avc_version = -1; ++ ++/* ++ * selinux_state ++ * ++ * It is deployed on the shared memory region, to show the system ++ * state of SELinux and its security policy. ++ */ ++struct ++{ ++ int version; ++ ++ bool enforcing; ++} *selinux_state = NULL; ++ ++Size ++sepgsql_shmem_size(void) ++{ ++ return sizeof(*selinux_state); ++} ++ ++static void ++sepgsql_shmem_init(void) ++{ ++ bool found; ++ ++ selinux_state = ShmemInitStruct("SELinux system state", ++ sepgsql_shmem_size(), &found); ++ if (!found) ++ { ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ ++ selinux_state->version = 0; ++ selinux_state->enforcing = (security_getenforce() > 0); ++ ++ LWLockRelease(SepgsqlAvcLock); ++ } ++} ++ ++void ++sepgsql_avc_switch(const char *scontext) ++{ ++ avc_page *new_page; ++ int i, length; ++ ++ if (current_page) ++ { ++ new_page = current_page; ++ do { ++ if (strcmp(new_page->scontext, scontext) == 0) ++ { ++ current_page = new_page; ++ return; ++ } ++ new_page = new_page->next; ++ } while (new_page != current_page); ++ } ++ ++ /* ++ * Not found, so create a new avc_page ++ */ ++ length = sizeof(avc_page) + strlen(scontext); ++ new_page = MemoryContextAllocZero(AvcMemCtx, length); ++ ++ strcpy(new_page->scontext, scontext); ++ for (i = 0; i < AVC_HASH_NUM_SLOTS; i++) ++ new_page->slot[i] = NIL; ++ ++ if (!current_page) ++ new_page->next = new_page; ++ else ++ { ++ new_page->next = current_page->next; ++ current_page->next = new_page; ++ } ++ current_page = new_page; ++} ++ ++ ++static void ++sepgsql_avc_reset(void) ++{ ++ Assert(AvcMemCtx != NULL); ++ ++ MemoryContextReset(AvcMemCtx); ++ ++ current_page = NULL; ++ ++ sepgsql_avc_switch(sepgsql_get_client_label()); ++} ++ ++static bool ++sepgsql_avc_is_valid(void) ++{ ++ bool result = true; ++ ++ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); ++ if (avc_version != selinux_state->version) ++ { ++ sepgsql_avc_reset(); ++ ++ /* copy current version to local variable */ ++ avc_version = selinux_state->version; ++ ++ result = false; ++ } ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return result; ++} ++ ++ ++static void ++sepgsql_avc_reclaim(avc_page *page) ++{ ++ ListCell *l; ++ ++ while (page->avc_count > AVC_HASH_NUM_NODES - 10) ++ { ++ foreach (l, page->slot[page->lru_hint]) ++ { ++ avc_datum *cache = lfirst(l); ++ ++ if (cache->hot_cache) ++ cache->hot_cache = false; ++ { ++ list_delete_ptr(page->slot[page->lru_hint], cache); ++ pfree(cache); ++ page->avc_count--; ++ } ++ } ++ page->lru_hint = (page->lru_hint + 1) % AVC_HASH_NUM_SLOTS; ++ } ++} ++ ++static avc_datum * ++sepgsql_avc_make_entry(avc_page *page, ++ sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) ++{ ++ struct av_decision avd; ++ MemoryContext oldctx; ++ char *scontext; ++ char *tcontext; ++ char *ncontext; ++ avc_datum *cache; ++ uint32 hash_key, index; ++ ++ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid); ++ index = hash_key % AVC_HASH_NUM_SLOTS; ++ ++ oldctx = MemoryContextSwitchTo(AvcMemCtx); ++ ++ scontext = page->scontext; ++ tcontext = seclabelRawOutput(tsid.relid, tsid.secid); ++ ncontext = sepgsql_compute_create(scontext, tcontext, tclass); ++ ++ sepgsql_compute_avd(scontext, tcontext, tclass, &avd); ++ ++ cache = palloc0(sizeof(avc_datum)); ++ ++ cache->hash_key = hash_key; ++ ++ cache->tclass = tclass; ++ ++ cache->hot_cache = true; ++ cache->tcontext = tcontext; ++ cache->ncontext = ncontext; ++ ++ cache->tsid.relid = tsid.relid; ++ cache->tsid.secid = tsid.secid; ++ ++ if (OidIsValid(nrelid)) ++ { ++ cache->nsid.relid = nrelid; ++ cache->nsid.secid = seclabelRawInput(nrelid, ncontext); ++ } ++ ++ cache->allowed = avd.allowed; ++ cache->auditallow = avd.auditallow; ++ cache->auditdeny = avd.auditdeny; ++ if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) ++ cache->permissive = true; ++ ++ if (page->avc_count > AVC_HASH_NUM_NODES) ++ sepgsql_avc_reclaim(page); ++ ++ page->slot[index] = lcons(cache, page->slot[index]); ++ page->avc_count++; ++ ++ MemoryContextSwitchTo(oldctx); ++ ++ return cache; ++} ++ ++static avc_datum * ++sepgsql_avc_lookup(avc_page *page, ++ sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) ++{ ++ avc_datum *cache = NULL; ++ uint32 hash_key, index; ++ ListCell *l; ++ ++ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid); ++ index = hash_key % AVC_HASH_NUM_SLOTS; ++ ++ foreach (l, page->slot[index]) ++ { ++ cache = lfirst(l); ++ if (cache->hash_key == hash_key && ++ cache->tclass == tclass && ++ cache->tsid.relid == tsid.relid && ++ cache->tsid.secid == tsid.secid && ++ cache->nsid.relid == nrelid) ++ { ++ cache->hot_cache = true; ++ return cache; ++ } ++ } ++ return NULL; ++} ++ ++bool ++sepgsql_client_perms(sepgsql_sid_t tsid, ++ uint16 tclass, uint32 required, ++ const char *audit_name, bool abort) ++{ ++ avc_datum *cache; ++ uint32 denied, audited; ++ bool result = true; ++ ++ do { ++ cache = sepgsql_avc_lookup(current_page, ++ tsid, tclass, InvalidOid); ++ if (!cache) ++ cache = sepgsql_avc_make_entry(current_page, ++ tsid, tclass, InvalidOid); ++ } while (!sepgsql_avc_is_valid()); ++ ++ denied = required & ~cache->allowed; ++ if (sepgsql_debug_audit && tclass != SEPG_CLASS_DB_TUPLE) ++ audited = (denied ? (denied & ~0) : (required & ~0)); ++ else ++ audited = (denied ? (denied & cache->auditdeny) ++ : (required & cache->auditallow)); ++ ++ if (audited) ++ { ++ sepgsql_audit_log(!!denied, ++ current_page->scontext, ++ seclabelRawOutput(tsid.relid, tsid.secid), ++ tclass, audited, audit_name); ++ } ++ ++ if (denied) ++ { ++ if (!sepgsql_get_enforce() || cache->permissive) ++ cache->allowed |= required; /* prevent flood of audit log */ ++ else ++ { ++ if (abort) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("SELinux: security policy violation"))); ++ result = false; ++ } ++ } ++ ++ return result; ++} ++ ++sepgsql_sid_t ++sepgsql_client_create_secid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) ++{ ++ avc_datum *cache; ++ ++ do { ++ cache = sepgsql_avc_lookup(current_page, tsid, tclass, nrelid); ++ ++ if (!cache) ++ cache = sepgsql_avc_make_entry(current_page, ++ tsid, tclass, nrelid); ++ } while (!sepgsql_avc_is_valid()); ++ ++ return cache->nsid; ++} ++ ++char * ++sepgsql_client_create_label(sepgsql_sid_t tsid, uint16 tclass) ++{ ++ avc_datum *cache; ++ ++ do { ++ cache = sepgsql_avc_lookup(current_page, tsid, tclass, InvalidOid); ++ ++ if (!cache) ++ cache = sepgsql_avc_make_entry(current_page, ++ tsid, tclass, InvalidOid); ++ } while (!sepgsql_avc_is_valid()); ++ ++ return cache->ncontext; ++} ++ ++static void ++sepgsql_avc_xact_callback(XactEvent event, void *arg) ++{ ++ if (event == XACT_EVENT_ABORT) ++ sepgsql_avc_reset(); ++} ++ ++static void ++sepgsql_avc_sub_xact_callback(SubXactEvent event, SubTransactionId mySubid, ++ SubTransactionId parentSubid, void *arg) ++{ ++ if (event == SUBXACT_EVENT_ABORT_SUB) ++ sepgsql_avc_reset(); ++} ++ ++void ++sepgsql_avc_init(void) ++{ ++ sepgsql_shmem_init(); ++ ++ AvcMemCtx = AllocSetContextCreate(TopMemoryContext, ++ "Userspace AVC", ++ ALLOCSET_DEFAULT_MINSIZE, ++ ALLOCSET_DEFAULT_INITSIZE, ++ ALLOCSET_DEFAULT_MAXSIZE); ++ /* ++ * userspace avc should be invalidate when the current transaction ++ * is aborted on errors, because sid to be created shall be rollbacked. ++ */ ++ RegisterXactCallback(sepgsql_avc_xact_callback, NULL); ++ RegisterSubXactCallback(sepgsql_avc_sub_xact_callback, NULL); ++} ++ ++/* ------------------------------------------------------------ ++ * ++ * SELinux state monitor process ++ * ++ * ------------------------------------------------------------ ++ */ ++static int ++sepgsql_cb_log(int type, const char *fmt, ...) ++{ ++ char *c, buffer[1024]; ++ va_list ap; ++ ++ va_start(ap, fmt); ++ vsnprintf(buffer, sizeof(buffer), fmt, ap); ++ va_end(ap); ++ ++ c = strrchr(buffer, '\n'); ++ if (c) ++ *c = '\0'; ++ ++ ereport(LOG,(errmsg("%s", buffer))); ++ ++ return 0; ++} ++ ++static int ++sepgsql_cb_setenforce(int enforce) ++{ ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ selinux_state->enforcing = (enforce ? true : false); ++ selinux_state->version++; ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return 0; ++} ++ ++static int ++sepgsql_cb_policyload(int seqno) ++{ ++ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); ++ selinux_state->version++; ++ LWLockRelease(SepgsqlAvcLock); ++ ++ return 0; ++} ++ ++void ++sepgsql_avc_worker_main(void) ++{ ++ union selinux_callback cb; ++ ++ Assert(sepgsql_is_enabled()); ++ ++#ifdef HAVE_SETSID ++ if (setsid() < 0) ++ elog(FATAL, "setsid() failed: %m"); ++#endif ++ ++ /* ++ * setup the signal handler ++ */ ++ pqinitmask(); ++ pqsignal(SIGHUP, SIG_IGN); ++ pqsignal(SIGINT, SIG_IGN); ++ pqsignal(SIGTERM, exit); ++ pqsignal(SIGQUIT, exit); ++ pqsignal(SIGUSR1, SIG_IGN); ++ pqsignal(SIGUSR2, SIG_IGN); ++ pqsignal(SIGCHLD, SIG_DFL); ++ PG_SETMASK(&UnBlockSig); ++ ++ /* ++ * map shared memory segment ++ */ ++ sepgsql_shmem_init(); ++ ++ ereport(LOG, (errmsg("SELinux: netlink receiver (pid=%u)", getpid()))); ++ ++ /* ++ * setup callback functions from avc_netlink_loop() ++ */ ++ cb.func_log = sepgsql_cb_log; ++ selinux_set_callback(SELINUX_CB_LOG, cb); ++ cb.func_setenforce = sepgsql_cb_setenforce; ++ selinux_set_callback(SELINUX_CB_SETENFORCE, cb); ++ cb.func_policyload = sepgsql_cb_policyload; ++ selinux_set_callback(SELINUX_CB_POLICYLOAD, cb); ++ ++ /* ++ * open netlink socket and wait for messages ++ */ ++ avc_netlink_open(1); ++ ++ avc_netlink_loop(); ++ ++ exit(0); ++} +diff --git a/src/backend/sepgsql/blob.c b/src/backend/sepgsql/blob.c +new file mode 100644 +index 0000000..e60e9cc +--- /dev/null ++++ b/src/backend/sepgsql/blob.c +@@ -0,0 +1,245 @@ ++/* ++ * blob.c ++ * ++ * SELinux hooks related to large objects ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/genam.h" ++#include "access/heapam.h" ++#include "access/sysattr.h" ++#include "catalog/indexing.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_largeobject_metadata.h" ++#include "catalog/pg_seclabel.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/fmgroids.h" ++#include "utils/tqual.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_largeobject_common(Oid loid, Snapshot snapshot, ++ uint32 required, bool abort) ++{ ++ Relation pg_lo_meta; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ char auname[NAMEDATALEN]; ++ bool retval; ++ ++ snprintf(auname, sizeof(auname), "blob_%u", loid); ++ ++ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, ++ AccessShareLock); ++ ++ ScanKeyInit(&skey, ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(loid)); ++ ++ scan = systable_beginscan(pg_lo_meta, ++ LargeObjectMetadataOidIndexId, true, ++ snapshot, 1, &skey); ++ ++ tuple = systable_getnext(scan); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "large object %u does not exist", loid); ++ ++ tsid.relid = LargeObjectMetadataRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_BLOB, ++ required, ++ auname, ++ abort); ++ systable_endscan(scan); ++ ++ heap_close(pg_lo_meta, AccessShareLock); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_largeobject_create(Oid loid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char auname[NAMEDATALEN]; ++ ++ nsid = sepgsql_get_default_blob_secid(MyDatabaseId); ++ snprintf(auname, sizeof(auname), "blob_%u", loid); ++ ++ /* db_blob:{create} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__CREATE, ++ auname, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_largeobject_alter(Oid loid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_largeobject_common(loid, SnapshotNow, ++ SEPG_DB_BLOB__SETATTR, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_largeobject_relabel(Oid loid, char *newLabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char auname[NAMEDATALEN]; ++ ++ nsid.relid = LargeObjectMetadataRelationId; ++ nsid.secid = seclabelTransInput(nsid.relid, newLabel); ++ ++ snprintf(auname, sizeof(auname), "blob_%u", loid); ++ ++ /* db_blob:{setattr relabelfrom} */ ++ sepgsql_largeobject_common(loid, ++ SnapshotNow, ++ SEPG_DB_BLOB__SETATTR | ++ SEPG_DB_BLOB__RELABELFROM, ++ true); ++ /* db_blob:{relabelto} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__RELABELTO, ++ auname, ++ true); ++ return nsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_largeobject_drop(Oid loid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_largeobject_common(loid, SnapshotNow, ++ SEPG_DB_BLOB__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_largeobject_read(Oid loid, Snapshot snapshot) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_largeobject_common(loid, snapshot, ++ SEPG_DB_BLOB__READ, true); ++ } ++#endif ++} ++ ++void ++sepgsql_largeobject_write(Oid loid, Snapshot snapshot) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_largeobject_common(loid, snapshot, ++ SEPG_DB_BLOB__WRITE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_largeobject_import(Oid loid, const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char auname[NAMEDATALEN]; ++ ++ nsid = sepgsql_get_default_blob_secid(MyDatabaseId); ++ snprintf(auname, sizeof(auname), "blob_%u", loid); ++ ++ /* db_blob:{create} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__CREATE | ++ SEPG_DB_BLOB__WRITE | ++ SEPG_DB_BLOB__IMPORT, ++ auname, ++ true); ++ /* db_file:{read} */ ++ // XXX - todo: add file read checks ++ ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_largeobject_export(Oid loid, Snapshot snapshot, const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_blob:{read export} */ ++ sepgsql_largeobject_common(loid, snapshot, ++ SEPG_DB_BLOB__READ | ++ SEPG_DB_BLOB__EXPORT, true); ++ /* file:{write} */ ++ // TODO: add security checks ++ } ++#endif ++} ++ ++void ++sepgsql_largeobject_grant(Oid loid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_largeobject_common(loid, SnapshotNow, ++ SEPG_DB_BLOB__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_largeobject_comment(Oid loid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_largeobject_common(loid, SnapshotNow, ++ SEPG_DB_BLOB__SETATTR, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/conversion.c b/src/backend/sepgsql/conversion.c +new file mode 100644 +index 0000000..6a60284 +--- /dev/null ++++ b/src/backend/sepgsql/conversion.c +@@ -0,0 +1,147 @@ ++/* ++ * conversion.c ++ * ++ * SELinux hooks related to conversion ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_conversion.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_conversion_common(Oid convOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(convOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for conversion %u", convOid); ++ ++ tsid.relid = ConversionRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_conversion_namespace(Oid convOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(convOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++#endif ++ ++Oid ++sepgsql_conversion_create(const char *convName, ++ Oid namespaceId, Oid conversionFunc) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ nsid = sepgsql_get_default_tuple_secid(ConversionRelationId); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME, true); ++ /* db_procedure:{install} */ ++ sepgsql_proc_common(conversionFunc, ++ SEPG_DB_PROCEDURE__INSTALL, true); ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ convName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_conversion_alter(Oid convOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_conversion_alter_rename(Oid convOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_conversion_namespace(convOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_conversion_drop(Oid convOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_conversion_namespace(convOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_conversion_comment(Oid convOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/database.c b/src/backend/sepgsql/database.c +new file mode 100644 +index 0000000..bee9d36 +--- /dev/null ++++ b/src/backend/sepgsql/database.c +@@ -0,0 +1,201 @@ ++/* ++ * database.c ++ * ++ * SELinux hooks related to database ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_database.h" ++#include "catalog/pg_seclabel.h" ++#include "commands/dbcommands.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_database_common(Oid datOid, uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(DATABASEOID, ++ ObjectIdGetDatum(datOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for database %u", datOid); ++ ++ tsid.relid = DatabaseRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_DATABASE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_database_create(const char *datName, Oid templateOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ /* compute a default security context */ ++ nsid = sepgsql_get_default_database_secid(templateOid); ++ ++ /* db_database:{create} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_DATABASE, ++ SEPG_DB_DATABASE__CREATE, ++ datName, true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_database_alter(Oid databaseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__SETATTR, ++ true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_database_relabel(Oid databaseOid, char *new_label) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char *auname; ++ ++ nsid.relid = DatabaseRelationId; ++ nsid.secid = seclabelTransInput(nsid.relid, new_label); ++ ++ auname = get_database_name(databaseOid); ++ ++ /* db_database:{setattr relabelfrom} */ ++ sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__SETATTR | ++ SEPG_DB_DATABASE__RELABELFROM, ++ true); ++ ++ /* db_database:{relabelto} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_DATABASE, ++ SEPG_DB_DATABASE__RELABELTO, ++ auname, ++ true); ++ pfree(auname); ++ ++ return nsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_database_drop(Oid databaseOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__DROP, ++ true); ++ } ++#endif ++} ++ ++void ++sepgsql_database_grant(Oid databaseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__SETATTR, ++ true); ++ } ++#endif ++} ++ ++void ++sepgsql_database_comment(Oid databaseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__SETATTR, ++ true); ++ } ++#endif ++} ++ ++void ++sepgsql_database_connect(Oid databaseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ if (!sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__ACCESS, ++ false)) ++ ereport(FATAL, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("permission denied for database \"%s\"", ++ get_database_name(databaseOid)))); ++ } ++#endif ++} ++ ++void ++sepgsql_database_reindex(Oid databaseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ // TODO: check db_table:{indexon} for each ++ ++ ++ ++ } ++#endif ++} ++ ++void ++sepgsql_database_getattr(Oid databaseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_database_common(databaseOid, ++ SEPG_DB_DATABASE__GETATTR, ++ true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/fdw.c b/src/backend/sepgsql/fdw.c +new file mode 100644 +index 0000000..be4a34b +--- /dev/null ++++ b/src/backend/sepgsql/fdw.c +@@ -0,0 +1,296 @@ ++/* ++ * fdw.c ++ * ++ * SELinux hooks related to foreign data wrapper ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_foreign_data_wrapper.h" ++#include "catalog/pg_foreign_server.h" ++#include "catalog/pg_user_mapping.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort) ++{ ++ Form_pg_foreign_data_wrapper fdwForm; ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ bool retval; ++ ++ tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for foreign-data-wrapper %u", fdwOid); ++ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple); ++ ++ tsid.relid = ForeignDataWrapperRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ NameStr(fdwForm->fdwname), ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++bool ++sepgsql_fserver_common(Oid fservOid, uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fservOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for foreign-server %u", fservOid); ++ ++ tsid.relid = ForeignServerRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_foreign_server) GETSTRUCT(tuple))->srvname); ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static char * ++get_fserver_name(Oid fservOid) ++{ ++ Form_pg_foreign_server servForm; ++ HeapTuple tuple; ++ char *srvname = NULL; ++ ++ tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fservOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ servForm = (Form_pg_foreign_server) GETSTRUCT(tuple); ++ ++ srvname = pstrdup(NameStr(servForm->srvname)); ++ ++ ReleaseSysCache(tuple); ++ } ++ return srvname; ++} ++ ++bool ++sepgsql_user_mapping_common(Oid umapOid, uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ Oid umuser; ++ Oid umserver; ++ char auname[NAMEDATALEN * 2 + 10]; ++ bool retval; ++ ++ tuple = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umapOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for user mapping %u", umapOid); ++ ++ tsid.relid = UserMappingRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ umuser = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umuser; ++ umserver = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umserver; ++ ++ snprintf(auname, sizeof(auname), "%s@%s", ++ OidIsValid(umuser) ? GetUserNameFromId(umuser) : "public", ++ get_fserver_name(umserver)); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_fdw_create(const char *fdwName, Oid validatorFunc) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(ForeignDataWrapperRelationId); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(validatorFunc)) ++ sepgsql_proc_common(validatorFunc, ++ SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ fdwName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_fdw_alter(Oid fdwOid, Oid newValidator) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_procedure:{install} */ ++ if (OidIsValid(newValidator)) ++ sepgsql_proc_common(newValidator, ++ SEPG_DB_PROCEDURE__INSTALL, true); ++ /* db_tuple:{update} */ ++ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_fdw_drop(Oid fdwOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{delete} */ ++ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_fdw_grant(Oid fdwOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_fserver_create(const char *fservName, Oid fdwOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(ForeignServerRelationId); ++ ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ fservName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_fserver_alter(Oid fservOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_fserver_drop(Oid fservOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_fserver_grant(Oid fservOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_user_mapping_create(Oid umuserId, Oid fservOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ char auname[NAMEDATALEN * 2 + 10]; ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(UserMappingRelationId); ++ ++ snprintf(auname, sizeof(auname), "%s@%s", ++ OidIsValid(umuserId) ? GetUserNameFromId(umuserId) : "public", ++ get_fserver_name(fservOid)); ++ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ auname, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_user_mapping_alter(Oid umapOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_user_mapping_common(umapOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_user_mapping_drop(Oid umapOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{delete} */ ++ sepgsql_user_mapping_common(umapOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/file.c b/src/backend/sepgsql/file.c +new file mode 100644 +index 0000000..9a9e8a3 +--- /dev/null ++++ b/src/backend/sepgsql/file.c +@@ -0,0 +1,287 @@ ++/* ++ * file.c ++ * ++ * SELinux hooks related to filesystem ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++ ++#include ++#ifdef HAVE_SELINUX ++#include ++#endif ++#include ++#include ++#include ++ ++#ifdef HAVE_SELINUX ++static uint16 ++sepgsql_file_class(const char *filename, bool abort) ++{ ++ struct stat stbuf; ++ uint16 tclass; ++ ++ if (stat(filename, &stbuf) < 0) ++ { ++ if (abort) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not stat file \"%s\": %m", filename))); ++ return 0; ++ } ++ ++ /* decide object class to be applied */ ++ 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; ++ ++ return tclass; ++} ++ ++static bool ++sepgsql_file_common(const char *filename, uint16 tclass, uint32 required, bool abort) ++{ ++ struct stat stbuf; ++ char *scontext = sepgsql_get_client_label(); ++ char *tcontext; ++ char *temp; ++ bool retval; ++ ++ if (stat(filename, &stbuf) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not stat file \"%s\"", filename))); ++ ++ if (getfilecon_raw(filename, &temp) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not get security context \"%s\"", filename))); ++ ++ PG_TRY(); ++ { ++ tcontext = pstrdup(temp); ++ } ++ PG_CATCH(); ++ { ++ freecon(temp); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(temp); ++ ++ retval = sepgsql_compute_perms(scontext, tcontext, ++ tclass, required, ++ filename, abort); ++ pfree(tcontext); ++ ++ return retval; ++} ++#endif ++ ++void ++sepgsql_file_getattr(const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ uint16 tclass = sepgsql_file_class(filename, true); ++ ++ sepgsql_file_common(filename, tclass, ++ SEPG_FILE__GETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_file_read(const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ uint16 tclass = sepgsql_file_class(filename, true); ++ ++ sepgsql_file_common(filename, tclass, ++ SEPG_FILE__READ, true); ++ } ++#endif ++} ++ ++void ++sepgsql_file_write(const char *filename, bool may_create) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ uint16 tclass = sepgsql_file_class(filename, !may_create); ++ char *dir_name; ++ char *tcontext; ++ char *ncontext; ++ char *temp; ++ ++ /* write an existing file */ ++ if (tclass != 0) ++ { ++ sepgsql_file_common(filename, tclass, ++ SEPG_FILE__WRITE, true); ++ return; ++ } ++ ++ /* create a new file, and write it */ ++ dir_name = dirname(pstrdup(filename)); ++ ++ if (sepgsql_file_class(dir_name, true) != SEPG_CLASS_DIR) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a directory", dir_name))); ++ ++ if (getfilecon(dir_name, &temp) < 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not get security context \"%s\"", dir_name))); ++ PG_TRY(); ++ { ++ tcontext = pstrdup(temp); ++ } ++ PG_CATCH(); ++ { ++ freecon(temp); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(temp); ++ ++ /* dir:{add_name} */ ++ sepgsql_file_common(dir_name, SEPG_CLASS_DIR, ++ SEPG_DIR__ADD_NAME, true); ++ ++ /* file:{create write} */ ++ ncontext = sepgsql_compute_create(sepgsql_get_client_label(), ++ tcontext, SEPG_CLASS_FILE); ++ sepgsql_compute_perms(sepgsql_get_client_label(), ++ ncontext, ++ SEPG_CLASS_FILE, ++ SEPG_FILE__CREATE | ++ SEPG_FILE__WRITE, ++ filename, true); ++ } ++#endif ++} ++ ++void ++sepgsql_file_append(const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ if (sepgsql_file_class(filename, true) != SEPG_CLASS_FILE) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a regular file", filename))); ++ ++ sepgsql_file_common(filename, SEPG_CLASS_FILE, ++ SEPG_FILE__APPEND, true); ++ } ++#endif ++} ++ ++void ++sepgsql_file_unlink(const char *filename) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ uint16 tclass = sepgsql_file_class(filename, true); ++ ++ sepgsql_file_common(filename, tclass, SEPG_FILE__UNLINK, true); ++ } ++#endif ++} ++ ++void ++sepgsql_file_rename(const char *oldpath, const char *newpath) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ char *olddir = dirname(pstrdup(oldpath)); ++ char *newdir = dirname(pstrdup(newpath)); ++ uint16 tclass; ++ uint32 required; ++ ++ /* file:{setattr} on oldpath */ ++ tclass = sepgsql_file_class(oldpath, true); ++ sepgsql_file_common(oldpath, tclass, ++ SEPG_FILE__RENAME, true); ++ ++ if (sepgsql_file_class(olddir, true) != SEPG_CLASS_DIR) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a directory", olddir))); ++ ++ required = SEPG_DIR__SEARCH | SEPG_DIR__REMOVE_NAME; ++ if (strcmp(olddir, newdir) == 0) ++ { ++ /* dir:{search remove_name add_name} */ ++ required |= SEPG_DIR__ADD_NAME; ++ sepgsql_file_common(olddir, SEPG_CLASS_DIR, required, true); ++ } ++ else ++ { ++ /* dir:{search remove_name (reparent)} */ ++ if (tclass == SEPG_CLASS_DIR) ++ required |= SEPG_DIR__REPARENT; ++ sepgsql_file_common(olddir, SEPG_CLASS_DIR, required, true); ++ ++ if (sepgsql_file_class(newdir, true) != SEPG_CLASS_DIR) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a directory", newdir))); ++ ++ /* dir:{add_name} */ ++ sepgsql_file_common(newdir, SEPG_CLASS_DIR, ++ SEPG_DIR__ADD_NAME, true); ++ } ++ ++ /* file:{unlink} or dir:{DIR__RMDIR}, if necessary */ ++ tclass = sepgsql_file_class(newpath, false); ++ if (tclass == SEPG_CLASS_DIR) ++ sepgsql_file_common(newpath, tclass, SEPG_DIR__RMDIR, true); ++ else if (tclass > 0) ++ sepgsql_file_common(newpath, tclass, SEPG_FILE__UNLINK, true); ++ } ++#endif ++} ++ ++void ++sepgsql_dir_list(const char *dirname) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ uint16 tclass = sepgsql_file_class(dirname, true); ++ ++ if (tclass != SEPG_CLASS_DIR) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not directory", dirname))); ++ ++ sepgsql_file_common(dirname, tclass, SEPG_DIR__READ, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/label.c b/src/backend/sepgsql/label.c +new file mode 100644 +index 0000000..7ab355c +--- /dev/null ++++ b/src/backend/sepgsql/label.c +@@ -0,0 +1,656 @@ ++/* ++ * label.c ++ * SE-PostgreSQL security label management ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/relscan.h" ++#include "access/xact.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_class.h" ++#include "catalog/pg_database.h" ++#include "catalog/pg_largeobject_metadata.h" ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_seclabel.h" ++#include "catalog/pg_type.h" ++#include "commands/dbcommands.h" ++#include "miscadmin.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/fmgroids.h" ++#include "utils/lsyscache.h" ++#include "utils/rel.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++#include // for workaround hack ++#include ++ ++/* ++ * guc parameter to turn on/off mcstrans ++ */ ++bool sepgsql_mcstrans; ++ ++sepgsql_sid_t ++sepgsql_move_secid(Oid dst_relid, sepgsql_sid_t ssid) ++{ ++ char *label; ++ sepgsql_sid_t dsid = { .relid = dst_relid, ++ .secid = InvalidOid }; ++ ++ label = seclabelRawOutput(ssid.relid, ssid.secid); ++ if (label) ++ { ++ dsid.secid = seclabelRawInput(dsid.relid, label); ++ ++ pfree(label); ++ } ++ return dsid; ++} ++ ++static sepgsql_sid_t ++get_default_secid_with_database(Oid relOid, Oid databaseOid, uint16 tclass) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t datsid; ++ ++ tuple = SearchSysCache1(DATABASEOID, ++ ObjectIdGetDatum(databaseOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for database: %u", databaseOid); ++ ++ datsid.relid = DatabaseRelationId; ++ datsid.secid = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ ++ return sepgsql_client_create_secid(datsid, tclass, relOid); ++} ++ ++static sepgsql_sid_t ++get_default_secid_with_schema(Oid relOid, Oid namespaceOid, uint16 tclass) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t nspsid; ++ ++ tuple = SearchSysCache1(NAMESPACEOID, ++ ObjectIdGetDatum(namespaceOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for namespace: %u", namespaceOid); ++ ++ nspsid.relid = NamespaceRelationId; ++ nspsid.secid = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ ++ return sepgsql_client_create_secid(nspsid, tclass, relOid); ++} ++ ++static sepgsql_sid_t ++get_default_secid_with_table(Oid relOid, Oid tableOid, uint16 tclass) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tblsid; ++ ++ tuple = SearchSysCache1(RELOID, ++ ObjectIdGetDatum(tableOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation: %u", tableOid); ++ ++ tblsid.relid = RelationRelationId; ++ tblsid.secid = HeapTupleGetSecid(tuple); ++ ++ ReleaseSysCache(tuple); ++ ++ return sepgsql_client_create_secid(tblsid, tclass, relOid); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_database_secid(Oid templateOid) ++{ ++ return get_default_secid_with_database(DatabaseRelationId, ++ templateOid, ++ SEPG_CLASS_DB_DATABASE); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_schema_secid(Oid databaseOid) ++{ ++ return get_default_secid_with_database(NamespaceRelationId, ++ databaseOid, ++ SEPG_CLASS_DB_SCHEMA); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_table_secid(Oid namespaceOid) ++{ ++ return get_default_secid_with_schema(RelationRelationId, ++ namespaceOid, ++ SEPG_CLASS_DB_TABLE); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_sequence_secid(Oid namespaceOid) ++{ ++ return get_default_secid_with_schema(RelationRelationId, ++ namespaceOid, ++ SEPG_CLASS_DB_SEQUENCE); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_view_secid(Oid namespaceOid) ++{ ++ return get_default_secid_with_schema(RelationRelationId, ++ namespaceOid, ++ SEPG_CLASS_DB_VIEW); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_proc_secid(Oid namespaceOid) ++{ ++ return get_default_secid_with_schema(ProcedureRelationId, ++ namespaceOid, ++ SEPG_CLASS_DB_PROCEDURE); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_column_secid(Oid tableOid) ++{ ++ return get_default_secid_with_table(AttributeRelationId, ++ tableOid, ++ SEPG_CLASS_DB_COLUMN); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_tuple_secid(Oid tableOid) ++{ ++ return get_default_secid_with_table(tableOid, ++ tableOid, ++ SEPG_CLASS_DB_TUPLE); ++} ++ ++sepgsql_sid_t ++sepgsql_get_default_blob_secid(Oid databaseOid) ++{ ++ return get_default_secid_with_database(LargeObjectMetadataRelationId, ++ databaseOid, ++ SEPG_CLASS_DB_BLOB); ++} ++ ++Oid ++sepgsql_get_default_secid(Relation rel, HeapTuple tuple) ++{ ++ Oid namespaceId; ++ sepgsql_sid_t nsid ++ = { .relid = RelationGetRelid(rel), .secid = InvalidOid }; ++ ++ switch (RelationGetRelid(rel)) ++ { ++ case DatabaseRelationId: ++ case RelationRelationId: ++ case AttributeRelationId: ++ elog(WARNING, "Bug? a new tuple without security id on \"%s\"", ++ RelationGetRelationName(rel)); ++ break; ++ ++ case NamespaceRelationId: ++ nsid = sepgsql_get_default_schema_secid(MyDatabaseId); ++ break; ++ ++ case ProcedureRelationId: ++ namespaceId = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; ++ nsid = sepgsql_get_default_proc_secid(namespaceId); ++ break; ++ ++ case LargeObjectMetadataRelationId: ++ nsid = sepgsql_get_default_blob_secid(MyDatabaseId); ++ break; ++ ++ default: ++ nsid = sepgsql_get_default_tuple_secid(RelationGetRelid(rel)); ++ break; ++ } ++ return nsid.secid; ++} ++ ++/* ++ * a workaround implementation until libselinux/refpolicy don't ++ * support db_schema or other object classes. ++ */ ++static struct { ++ uint16 tclass; ++ char *pattern; ++ char *context; ++} initial_label_catalog[] = { ++ {SEPG_CLASS_DB_DATABASE, "*", ++ "system_u:object_r:sepgsql_db_t:s0"}, ++ {SEPG_CLASS_DB_SCHEMA, "*.*", ++ "system_u:object_r:sepgsql_db_t:s0"}, ++ {SEPG_CLASS_DB_TABLE, "*.pg_catalog.*", ++ "system_u:object_r:sepgsql_sysobj_t:s0"}, ++ {SEPG_CLASS_DB_TABLE, "*.*.*", ++ "system_u:object_r:sepgsql_table_t:s0"}, ++ {SEPG_CLASS_DB_VIEW, "*.*.*", ++ "system_u:object_r:sepgsql_db_t:s0"}, ++ {SEPG_CLASS_DB_SEQUENCE, "*.*.*", ++ "system_u:object_r:sepgsql_db_t:s0"}, ++ {SEPG_CLASS_DB_PROCEDURE, "*.pg_catalog.*", ++ "system_u:object_r:sepgsql_proc_exec_t:s0"}, ++ {SEPG_CLASS_DB_PROCEDURE, "*.*.*", ++ "system_u:object_r:sepgsql_user_proc_exec_t:s0"}, ++ {SEPG_CLASS_DB_COLUMN, "*.pg_catalog.*.*", ++ "system_u:object_r:sepgsql_sysobj_t:s0"}, ++ {SEPG_CLASS_DB_COLUMN, "*.*.*.*", ++ "system_u:object_r:sepgsql_table_t:s0"}, ++ {SEPG_CLASS_DB_TUPLE, "*.pg_catalog.*", ++ "system_u:object_r:sepgsql_sysobj_t:s0"}, ++ {SEPG_CLASS_DB_TUPLE, "*.*.*", ++ "system_u:object_r:sepgsql_table_t:s0"}, ++ {SEPG_CLASS_DB_BLOB, "*.*", ++ "system_u:object_r:sepgsql_blob_t:s0"}, ++ {0, NULL, NULL}, ++}; ++ ++static char * ++lookup_init_catalog(uint16 tclass, const char *name) ++{ ++ int i; ++ ++ for (i = 0; initial_label_catalog[i].pattern; i++) ++ { ++ if (initial_label_catalog[i].tclass == tclass && ++ fnmatch(initial_label_catalog[i].pattern, name, 0) == 0) ++ return initial_label_catalog[i].context; ++ } ++ elog(ERROR, "no valid initial security context for %s (tclass=%d)", ++ name, tclass); ++ return NULL; /* for compiler quiet */ ++} ++ ++static char * ++lookup_init_tuple_label(Oid relOid, HeapTuple tuple) ++{ ++ Oid relNsp = get_rel_namespace(relOid); ++ char namebuf[NAMEDATALEN * 3 + 10]; ++ ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(relNsp), ++ get_rel_name(relOid)); ++ ++ return lookup_init_catalog(SEPG_CLASS_DB_TUPLE, namebuf); ++} ++ ++static char * ++lookup_init_database_label(HeapTuple tuple) ++{ ++ Form_pg_database datForm = (Form_pg_database) GETSTRUCT(tuple); ++ ++ return lookup_init_catalog(SEPG_CLASS_DB_DATABASE, ++ NameStr(datForm->datname)); ++} ++ ++static char * ++lookup_init_schema_label(HeapTuple tuple) ++{ ++ Form_pg_namespace nspForm = (Form_pg_namespace) GETSTRUCT(tuple); ++ char namebuf[NAMEDATALEN * 2 + 10]; ++ ++ snprintf(namebuf, sizeof(namebuf), "%s.%s", ++ get_database_name(MyDatabaseId), ++ NameStr(nspForm->nspname)); ++ ++ return lookup_init_catalog(SEPG_CLASS_DB_SCHEMA, namebuf); ++} ++ ++static char * ++lookup_init_relation_label(HeapTuple tuple) ++{ ++ Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); ++ const char *relName = NameStr(classForm->relname); ++ Oid relNsp = classForm->relnamespace; ++ char *seclabel; ++ char namebuf[NAMEDATALEN * 3 + 10]; ++ ++ switch (classForm->relkind) ++ { ++ case RELKIND_RELATION: ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(relNsp), relName); ++ seclabel = lookup_init_catalog(SEPG_CLASS_DB_TABLE, namebuf); ++ break; ++ ++ case RELKIND_SEQUENCE: ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(relNsp), relName); ++ seclabel = lookup_init_catalog(SEPG_CLASS_DB_SEQUENCE, namebuf); ++ break; ++ ++ case RELKIND_VIEW: ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(relNsp), relName); ++ seclabel = lookup_init_catalog(SEPG_CLASS_DB_VIEW, namebuf); ++ break; ++ ++ case RELKIND_INDEX: { ++ HeapTuple tbltup; ++ HeapTuple indtup; ++ Oid tblOid; ++ Oid indOid = HeapTupleGetOid(tuple); ++ ++ indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indOid)); ++ if (!HeapTupleIsValid(indtup)) ++ elog(ERROR, "cache lookup failed for index %u", indOid); ++ ++ tblOid = ((Form_pg_index) GETSTRUCT(indtup))->indrelid; ++ tbltup = SearchSysCache1(RELOID, ObjectIdGetDatum(tblOid)); ++ if (!HeapTupleIsValid(tbltup)) ++ elog(ERROR, "cache lookup failed for relation %u", tblOid); ++ ++ seclabel = lookup_init_relation_label(tbltup); ++ ++ ReleaseSysCache(tbltup); ++ ReleaseSysCache(indtup); ++ ++ break; ++ } ++ case RELKIND_TOASTVALUE: { ++ HeapTuple tbltup; ++ Oid tblOid; ++ ++ /* ++ * XXX - we assume all the toast relation's name is ++ * "pg_toast_%u", and the "%u" shall be replaced by OID ++ * of the relation which owns the toast relation ++ */ ++ tblOid = strtoul(relName + 9, NULL, 10); ++ ++ tbltup = SearchSysCache1(RELOID, ++ ObjectIdGetDatum(tblOid)); ++ if (!HeapTupleIsValid(tbltup)) ++ elog(ERROR, "cache lookup failed for relation %u", tblOid); ++ ++ seclabel = lookup_init_relation_label(tbltup); ++ ++ ReleaseSysCache(tbltup); ++ ++ break; ++ } ++ case RELKIND_COMPOSITE_TYPE: { ++ Oid typOid = classForm->reltype; ++ HeapTuple typtup; ++ ++ typtup = SearchSysCache1(TYPEOID, ++ ObjectIdGetDatum(typOid)); ++ if (!HeapTupleIsValid(typtup)) ++ elog(ERROR, "cache lookup failed for type %u", typOid); ++ ++ seclabel = lookup_init_tuple_label(TypeRelationId, typtup); ++ ++ ReleaseSysCache(typtup); ++ ++ break; ++ } ++ default: ++ elog(ERROR, "unexpected relkind %c of \"%s\"", ++ classForm->relkind, relName); ++ seclabel = NULL; /* compiler quiet */ ++ break; ++ } ++ return seclabel; ++} ++ ++static char * ++lookup_init_attribute_label(HeapTuple tuple) ++{ ++ Form_pg_attribute attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ Oid tblOid = attForm->attrelid; ++ char *seclabel; ++ char namebuf[NAMEDATALEN * 4 + 10]; ++ ++ if (get_rel_relkind(tblOid) == RELKIND_RELATION) ++ { ++ Oid tblNsp = get_rel_namespace(tblOid); ++ ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(tblNsp), ++ get_rel_name(tblOid), ++ NameStr(attForm->attname)); ++ ++ seclabel = lookup_init_catalog(SEPG_CLASS_DB_COLUMN, namebuf); ++ } ++ else ++ { ++ HeapTuple tbltup; ++ ++ tbltup = SearchSysCache1(RELOID, ObjectIdGetDatum(tblOid)); ++ if (!HeapTupleIsValid(tbltup)) ++ elog(ERROR, "cache lookup failed for relation %u", tblOid); ++ ++ seclabel = lookup_init_relation_label(tbltup); ++ ++ ReleaseSysCache(tbltup); ++ } ++ return seclabel; ++} ++ ++static char * ++lookup_init_procedure_label(HeapTuple tuple) ++{ ++ Form_pg_proc proForm = (Form_pg_proc) GETSTRUCT(tuple); ++ Oid proNsp = proForm->pronamespace; ++ char namebuf[NAMEDATALEN * 3 + 10]; ++ ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(proNsp), ++ NameStr(proForm->proname)); ++ ++ return lookup_init_catalog(SEPG_CLASS_DB_PROCEDURE, namebuf); ++} ++ ++static char * ++lookup_init_largeobject_label(HeapTuple tuple) ++{ ++ char namebuf[NAMEDATALEN + 20]; ++ ++ snprintf(namebuf, sizeof(namebuf), "%s.%u", ++ get_database_name(MyDatabaseId), ++ HeapTupleGetOid(tuple)); ++ ++ return lookup_init_catalog(SEPG_CLASS_DB_BLOB, namebuf); ++} ++ ++void ++sepgsql_initial_labeling(void) ++{ ++ Relation classRel; ++ SysScanDesc classScan; ++ ScanKeyData classSkey; ++ HeapTuple classTup; ++ Relation rel; ++ HeapScanDesc scan; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ ++ Assert(IsBootstrapProcessingMode()); ++ ++ StartTransactionCommand(); ++ ++ classRel = heap_open(RelationRelationId, AccessShareLock); ++ ++ ScanKeyInit(&classSkey, ++ Anum_pg_class_relhassecids, ++ BTEqualStrategyNumber, F_BOOLEQ, ++ BoolGetDatum(true)); ++ ++ classScan = systable_beginscan(classRel, InvalidOid, false, ++ SnapshotNow, 1, &classSkey); ++ ++ while (HeapTupleIsValid(classTup = systable_getnext(classScan))) ++ { ++ Oid relOid = HeapTupleGetOid(classTup); ++ ++ Assert(((Form_pg_class) GETSTRUCT(classTup))->relhassecids); ++ ++ rel = heap_open(relOid, RowExclusiveLock); ++ ++ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); ++ ++ while (HeapTupleIsValid(oldtup = heap_getnext(scan, ForwardScanDirection))) ++ { ++ char *label; ++ Oid secid; ++ ++ switch (relOid) ++ { ++ case DatabaseRelationId: ++ label = lookup_init_database_label(oldtup); ++ break; ++ ++ case NamespaceRelationId: ++ label = lookup_init_schema_label(oldtup); ++ break; ++ ++ case RelationRelationId: ++ label = lookup_init_relation_label(oldtup); ++ break; ++ ++ case AttributeRelationId: ++ label = lookup_init_attribute_label(oldtup); ++ break; ++ ++ case ProcedureRelationId: ++ label = lookup_init_procedure_label(oldtup); ++ break; ++ ++ case LargeObjectMetadataRelationId: ++ label = lookup_init_largeobject_label(oldtup); ++ break; ++ ++ default: ++ label = lookup_init_tuple_label(relOid, oldtup); ++ break; ++ } ++ /* ++ * inplace-updating ++ */ ++ newtup = heap_copytuple(oldtup); ++ ++ secid = seclabelTransInput(relOid, label); ++ ++ HeapTupleSetSecid(newtup, secid); ++ ++ heap_inplace_update(rel, newtup); ++ ++ heap_freetuple(newtup); ++ } ++ heap_endscan(scan); ++ ++ heap_close(rel, RowExclusiveLock); ++ } ++ systable_endscan(classScan); ++ ++ heap_close(classRel, AccessShareLock); ++ ++ CommitTransactionCommand(); ++} ++ ++char * ++sepgsql_mcstrans_in(char *trans_label) ++{ ++ security_context_t raw_label; ++ security_context_t result; ++ ++ if (!sepgsql_mcstrans) ++ return trans_label; ++ ++ if (selinux_trans_to_raw_context(trans_label, &raw_label) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux: unable to translate \"%s\"", trans_label))); ++ PG_TRY(); ++ { ++ result = pstrdup(raw_label); ++ } ++ PG_CATCH(); ++ { ++ freecon(raw_label); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(raw_label); ++ ++ return result; ++} ++ ++char * ++sepgsql_mcstrans_out(char *raw_label) ++{ ++ security_context_t trans_label; ++ security_context_t result; ++ ++ if (!sepgsql_mcstrans) ++ return raw_label; ++ ++ if (selinux_raw_to_trans_context(raw_label, &trans_label) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux: unable to translate \"%s\"", raw_label))); ++ PG_TRY(); ++ { ++ result = pstrdup(trans_label); ++ } ++ PG_CATCH(); ++ { ++ freecon(trans_label); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(trans_label); ++ ++ return result; ++} ++ ++char * ++sepgsql_rawlabel_in(char *label) ++{ ++ if (!label || security_check_context_raw(label) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_NAME), ++ errmsg("invalid security context \"%s\"", label))); ++ return label; ++} ++ ++char * ++sepgsql_rawlabel_out(char *label) ++{ ++ if (!label || security_check_context_raw(label) < 0) ++ { ++ security_context_t unlabeled_label; ++ ++ if (security_get_initial_context_raw("unlabeled", ++ &unlabeled_label) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("unable to get unlabeled security context"))); ++ PG_TRY(); ++ { ++ label = pstrdup(unlabeled_label); ++ } ++ PG_CATCH(); ++ { ++ freecon(unlabeled_label); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(unlabeled_label); ++ } ++ return label; ++} +diff --git a/src/backend/sepgsql/misc.c b/src/backend/sepgsql/misc.c +new file mode 100644 +index 0000000..377a72b +--- /dev/null ++++ b/src/backend/sepgsql/misc.c +@@ -0,0 +1,147 @@ ++/* ++ * misc.c ++ * ++ * SELinux hooks related to misc features ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "libpq/libpq-be.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/builtins.h" ++#include "utils/fmgroids.h" ++#include "utils/guc.h" ++ ++#include ++ ++/* ++ * sepgsql_client_label ++ * ++ * security context of the peer process ++ */ ++static char *sepgsql_client_label = NULL; ++ ++char * ++sepgsql_get_client_label(void) ++{ ++ return sepgsql_client_label; ++} ++ ++char * ++sepgsql_set_client_label(char *new_label) ++{ ++ char *old_label = sepgsql_client_label; ++ ++ sepgsql_client_label = new_label; ++ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* ++ * NOTE: When an error happened prior to initialization of ++ * SE-PostgreSQL, NULL maight be delivered. In this case, ++ * we don't need to reset avc. ++ */ ++ if (new_label != NULL) ++ sepgsql_avc_switch(new_label); ++ } ++#endif ++ return old_label; ++} ++ ++void ++sepgsql_post_bootstraping(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ sepgsql_initial_labeling(); ++#endif ++} ++ ++void ++sepgsql_initialize(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ security_context_t context; ++ ++ /* init userspace avc */ ++ sepgsql_avc_init(); ++ ++ /* init privilege of the client */ ++ if (!MyProcPort) ++ { ++ /* ++ * SE-PgSQL does not prevent anything in single-user mode. ++ */ ++ sepostgresql_mode = SEPGSQL_MODE_INTERNAL; ++ ++ if (getprevcon_raw(&context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux: could not get server context"))); ++ } ++ else ++ { ++ if (getpeercon_raw(MyProcPort->sock, &context) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux: could not get client context"))); ++ } ++ sepgsql_set_client_label(context); ++ ++ return; ++ } ++#endif ++ if (default_with_secids) ++ { ++ default_with_secids = false; ++ elog(LOG, "guc: default_with_secid was turned off " ++ "because no label based access control is availabel now"); ++ } ++} ++ ++bool ++sepgsql_worker_needed(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ return true; ++#endif ++ return false; ++} ++ ++void ++sepgsql_worker_main(void) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_avc_worker_main(); ++ return; ++ } ++#endif ++ elog(FATAL, "Bug? try to launch worker process without security provider"); ++} ++ ++Datum ++sepgsql_getcon(PG_FUNCTION_ARGS) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ char *label = sepgsql_get_client_label(); ++ ++ PG_RETURN_TEXT_P(cstring_to_text(label)); ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ PG_RETURN_NULL(); ++} +diff --git a/src/backend/sepgsql/operator.c b/src/backend/sepgsql/operator.c +new file mode 100644 +index 0000000..7960525 +--- /dev/null ++++ b/src/backend/sepgsql/operator.c +@@ -0,0 +1,454 @@ ++/* ++ * operator.c ++ * ++ * SELinux hooks related to operators ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_operator.h" ++#include "catalog/pg_opclass.h" ++#include "catalog/pg_opfamily.h" ++#include "catalog/pg_seclabel.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/builtins.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_operator_common(Oid operOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ char *auname; ++ bool retval; ++ ++ tsid.relid = OperatorRelationId; ++ tsid.secid = GetSysCacheSecid1(OPEROID, ObjectIdGetDatum(operOid)); ++ ++ auname = format_operator(operOid); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ pfree(auname); ++ ++ return retval; ++} ++ ++bool ++sepgsql_opclass_common(Oid opcOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for operator class %u", opcOid); ++ ++ tsid.relid = OperatorClassRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++bool ++sepgsql_opfamily_common(Oid opfOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for operator family %u", opfOid); ++ ++ tsid.relid = OperatorFamilyRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_operator_namespace(Oid operOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId; ++ ++ tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_operator) GETSTRUCT(tuple))->oprnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++static Oid ++get_opclass_namespace(Oid opcOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++static Oid ++get_opfamily_namespace(Oid opfOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++#endif ++ ++Oid ++sepgsql_operator_create(const char *operName, Oid replaced, Oid namespaceId, ++ Oid codeFunc, Oid restrictFunc, Oid joinFunc, ++ Oid commutatorOp, Oid negatorOp) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ uint32 required; ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_tuple:{insert} */ ++ if (OidIsValid(replaced)) ++ { ++ nsid.relid = OperatorRelationId; ++ nsid.secid = GetSysCacheSecid1(OPEROID, ++ ObjectIdGetDatum(replaced)); ++ required = SEPG_DB_TUPLE__UPDATE; ++ } ++ else ++ { ++ nsid = sepgsql_get_default_tuple_secid(OperatorRelationId); ++ required = SEPG_DB_TUPLE__INSERT; ++ } ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ operName, ++ true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(codeFunc)) ++ sepgsql_proc_common(codeFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(restrictFunc)) ++ sepgsql_proc_common(restrictFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(joinFunc)) ++ sepgsql_proc_common(joinFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* ++ * XXX - we should check anything on he commutatorOp/negatorOp ++ */ ++ ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_operator_alter(Oid operOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_operator_common(operOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_operator_relabel(Oid operOid, char *newLabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ nsid.relid = OperatorRelationId; ++ nsid.secid = seclabelTransInput(OperatorRelationId, newLabel); ++ ++ /* db_tuple:{update relabelfrom} */ ++ sepgsql_operator_common(operOid, ++ SEPG_DB_TUPLE__UPDATE | ++ SEPG_DB_TUPLE__RELABELFROM, ++ true); ++ ++ /* db_tuple:{relabelto} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELTO, ++ format_operator(operOid), ++ true); ++ return nsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_operator_drop(Oid operOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_operator_namespace(operOid); ++ ++ /* db_namespace:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_operator_common(operOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_operator_comment(Oid operOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_operator_common(operOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_opclass_create(const char *opcName, Oid namespaceId, ++ Oid typeOid, Oid opfamilyOid, Oid storageOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_TUPLE__UPDATE, true); ++ ++ /* db_tuple:{insert} */ ++ nsid = sepgsql_get_default_tuple_secid(OperatorRelationId); ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ opcName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_opclass_alter(Oid opcOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_opclass_alter_rename(Oid opcOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_opclass_namespace(opcOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_opclass_drop(Oid opcOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_opclass_namespace(opcOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{drop} */ ++ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_opclass_comment(Oid opcOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_opfamily_create(const char *opfName, Oid namespaceId, Oid amOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_tuple:{insert} */ ++ nsid = sepgsql_get_default_tuple_secid(OperatorFamilyRelationId); ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ opfName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_opfamily_alter(Oid opfOid, bool isDrop, Oid amOid, ++ List *operators, List *procedures) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); ++ ++ /* XXX - to do we should install checks? */ ++ } ++#endif ++} ++ ++void ++sepgsql_opfamily_alter_rename(Oid opfOid, const char *newName) ++{ ++ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_opfamily_namespace(opfOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_opfamily_alter_owner(Oid opfOid, Oid newOwner) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_opfamily_drop(Oid opfOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_opfamily_namespace(opfOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_opfamily_comment(Oid opfOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/proc.c b/src/backend/sepgsql/proc.c +new file mode 100644 +index 0000000..1149571 +--- /dev/null ++++ b/src/backend/sepgsql/proc.c +@@ -0,0 +1,366 @@ ++/* ++ * proc.c ++ * ++ * SELinux hooks related to procedures ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_aggregate.h" ++#include "catalog/pg_language.h" ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_seclabel.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/builtins.h" ++#include "utils/syscache.h" ++#include "utils/lsyscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_proc_common(Oid procOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ char *auname; ++ bool retval; ++ ++ tsid.relid = ProcedureRelationId; ++ tsid.secid = GetSysCacheSecid1(PROCOID, ++ ObjectIdGetDatum(procOid)); ++ ++ auname = format_procedure(procOid); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_PROCEDURE, ++ required, ++ auname, ++ abort); ++ pfree(auname); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_proc_create(const char *proName, Oid replaced, ++ Oid namespaceId, Oid languageId) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ HeapTuple tuple; ++ sepgsql_sid_t nsid; ++ char *scontext; ++ char *tcontext; ++ uint32 required; ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{create or setattr} */ ++ if (OidIsValid(replaced)) ++ { ++ nsid.relid = ProcedureRelationId; ++ nsid.secid = GetSysCacheSecid1(PROCOID, ++ ObjectIdGetDatum(replaced)); ++ required = SEPG_DB_PROCEDURE__SETATTR; ++ } ++ else ++ { ++ nsid = sepgsql_get_default_proc_secid(namespaceId); ++ required = SEPG_DB_PROCEDURE__CREATE; ++ } ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_PROCEDURE, ++ required, ++ proName, ++ true); ++ ++ /* db_language:{implemente} */ ++ tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(languageId)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for language %u", languageId); ++ ++ scontext = seclabelRawOutput(LanguageRelationId, ++ HeapTupleGetSecid(tuple)); ++ tcontext = seclabelRawOutput(nsid.relid, nsid.secid); ++ ++ sepgsql_compute_perms(scontext, tcontext, ++ SEPG_CLASS_DB_LANGUAGE, ++ SEPG_DB_LANGUAGE__IMPLEMENTE, ++ proName, true); ++ ++ ReleaseSysCache(tuple); ++ ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_proc_alter(Oid procOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_proc_alter_rename(Oid procOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_func_namespace(procOid); ++ ++ /* db_schema:{remove_name add_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__REMOVE_NAME | ++ SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{setattr} */ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_proc_alter_schema(Oid procOid, Oid newSchema) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_func_namespace(procOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(newSchema, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{setattr} */ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_proc_relabel(Oid procOid, char *new_label) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ ++ tsid.relid = ProcedureRelationId; ++ tsid.secid = seclabelTransInput(tsid.relid, new_label); ++ ++ /* db_procedure:{setattr relabelfrom} */ ++ sepgsql_proc_common(procOid, ++ SEPG_DB_PROCEDURE__SETATTR | ++ SEPG_DB_PROCEDURE__RELABELFROM, true); ++ ++ /* db_procedure:{relabelto} */ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_DB_PROCEDURE__RELABELTO, ++ format_procedure(procOid), ++ true); ++ return tsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_proc_drop(Oid procOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_func_namespace(procOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_procedure:{proc} */ ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__DROP, true); ++ } ++#endif ++} ++ ++void ++sepgsql_proc_grant(Oid procOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_proc_comment(Oid procOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_proc_execute(Oid procOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__EXECUTE, true); ++ } ++#endif ++} ++ ++bool ++sepgsql_proc_be_inlined(HeapTuple protup) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ char *old_label; ++ char *new_label; ++ const char *auname ++ = NameStr(((Form_pg_proc) GETSTRUCT(protup))->proname); ++ ++ tsid.relid = ProcedureRelationId; ++ tsid.secid = HeapTupleGetSecid(protup); ++ ++ if (!sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_DB_PROCEDURE__EXECUTE, ++ auname, false)) ++ return false; ++ ++ old_label = sepgsql_get_client_label(); ++ new_label = sepgsql_client_create_label(tsid, SEPG_CLASS_PROCESS); ++ if (strcmp(old_label, new_label) != 0) ++ return false; ++ ++ return true; ++ } ++#endif ++ return true; ++} ++ ++char * ++sepgsql_proc_domtrans(HeapTuple protup, MemoryContext mcxt) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ char *old_label = sepgsql_get_client_label(); ++ char *new_label; ++ char *auname ++ = NameStr(((Form_pg_proc) GETSTRUCT(protup))->proname); ++ ++ tsid.relid = ProcedureRelationId; ++ tsid.secid = HeapTupleGetSecid(protup); ++ ++ new_label = sepgsql_client_create_label(tsid, SEPG_CLASS_PROCESS); ++ ++ if (strcmp(old_label, new_label) == 0) ++ return NULL; ++ ++ /* db_procedure:{entrypoint} */ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_DB_PROCEDURE__ENTRYPOINT, ++ auname, ++ true); ++ ++ /* db_process:{transition} */ ++ sepgsql_compute_perms(old_label, ++ new_label, ++ SEPG_CLASS_PROCESS, ++ SEPG_PROCESS__TRANSITION, ++ NULL, ++ true); ++ ++ return MemoryContextStrdup(mcxt, new_label); ++ } ++#endif ++ return NULL; ++} ++ ++Oid ++sepgsql_aggregate_create(const char *aggName, Oid namespaceId, ++ Oid transFunc, Oid finalFunc) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid securityId; ++ ++ /* check normal creation permission */ ++ securityId = sepgsql_proc_create(aggName, InvalidOid, ++ namespaceId, INTERNALlanguageId); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(transFunc)) ++ sepgsql_proc_common(transFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(finalFunc)) ++ sepgsql_proc_common(finalFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return securityId; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_aggregate_execute(Oid aggOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Form_pg_aggregate aggForm; ++ HeapTuple tuple; ++ ++ tuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for aggregate %u", aggOid); ++ ++ aggForm = (Form_pg_aggregate) GETSTRUCT(tuple); ++ ++ /* pg_proc:{execute} */ ++ sepgsql_proc_common(aggOid, SEPG_DB_PROCEDURE__EXECUTE, true); ++ ++ /* pg_proc:{execute} */ ++ if (OidIsValid(aggForm->aggtransfn)) ++ sepgsql_proc_common(aggForm->aggtransfn, ++ SEPG_DB_PROCEDURE__EXECUTE, true); ++ ++ /* pg_proc:{execute} */ ++ if (OidIsValid(aggForm->aggfinalfn)) ++ sepgsql_proc_common(aggForm->aggfinalfn, ++ SEPG_DB_PROCEDURE__EXECUTE, true); ++ ++ ReleaseSysCache(tuple); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/relation.c b/src/backend/sepgsql/relation.c +new file mode 100644 +index 0000000..2604749 +--- /dev/null ++++ b/src/backend/sepgsql/relation.c +@@ -0,0 +1,819 @@ ++/* ++ * relation.c ++ * ++ * SELinux hooks related to relation ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/sysattr.h" ++#include "catalog/catalog.h" ++#include "catalog/heap.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_class.h" ++#include "catalog/pg_seclabel.h" ++#include "catalog/pg_type.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_relation_common(Oid relOid, uint32 required, bool abort) ++{ ++ Form_pg_class classForm; ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ uint16 tclass; ++ bool retval; ++ ++ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relOid); ++ classForm = (Form_pg_class) GETSTRUCT(tuple); ++ ++ tsid.relid = RelationRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ switch (classForm->relkind) ++ { ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ break; ++ ++ case RELKIND_COMPOSITE_TYPE: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ ++ default: ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ } ++ ++ retval = sepgsql_client_perms(tsid, ++ tclass, ++ required, ++ NameStr(classForm->relname), ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Bitmapset * ++fixup_whole_row_reference(Oid relOid, int natts, Bitmapset *columns) ++{ ++ Bitmapset *result; ++ AttrNumber attno; ++ ++ attno = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber; ++ if (!bms_is_member(attno, columns)) ++ return columns; /* no need to fixup */ ++ ++ result = bms_copy(columns); ++ result = bms_del_member(result, attno); ++ ++ for (attno=1; attno <= natts; attno++) ++ { ++ Form_pg_attribute attForm; ++ HeapTuple atttup; ++ ++ atttup = SearchSysCache2(ATTNUM, ++ ObjectIdGetDatum(relOid), ++ Int16GetDatum(attno)); ++ if (!HeapTupleIsValid(atttup)) ++ continue; ++ ++ attForm = (Form_pg_attribute) GETSTRUCT(atttup); ++ if (!attForm->attisdropped) ++ { ++ int cindex = attno - FirstLowInvalidHeapAttributeNumber; ++ result = bms_add_member(result, cindex); ++ } ++ ReleaseSysCache(atttup); ++ } ++ ++ return result; ++} ++#endif ++ ++bool ++sepgsql_relation_perms(Oid relOid, AclMode aclmask, ++ Bitmapset *selectedCols, ++ Bitmapset *modifiedCols, bool abort) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Form_pg_class relForm; ++ HeapTuple tuple; ++ sepgsql_sid_t rsid; ++ Bitmapset *selColsEx; ++ Bitmapset *modColsEx; ++ Bitmapset *columns; ++ AttrNumber nattrs; ++ AttrNumber attno; ++ const char *auname; ++ char relkind; ++ uint16 tclass = 0; ++ uint32 required = 0; ++ bool rc = true; ++ ++ /* ++ * Hardwired policy: ++ * SE-PostgreSQL enforces clients cannot modify system catalogs ++ * and access toast values using DML statements in enforcing mode. ++ * Note that it performs in permissive mode during initdb phase. ++ */ ++ if (sepgsql_get_enforce()) ++ { ++ if (IsSystemNamespace(get_rel_namespace(relOid)) && ++ (aclmask & (ACL_UPDATE | ACL_INSERT | ACL_DELETE)) != 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("not allowed to modify system catalog \"%s\"", ++ get_rel_name(relOid)))); ++ ++ if (get_rel_relkind(relOid) == RELKIND_TOASTVALUE) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("not allowed to access toast values \"%s\"", ++ get_rel_name(relOid)))); ++ } ++ ++ /* ++ * check relation's permissions ++ */ ++ tuple = SearchSysCache1(RELOID, ++ ObjectIdGetDatum(relOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for relation %u", relOid); ++ ++ rsid.relid = RelationRelationId; ++ rsid.secid = HeapTupleGetSecid(tuple); ++ ++ relForm = (Form_pg_class) GETSTRUCT(tuple); ++ auname = NameStr(relForm->relname); ++ relkind = relForm->relkind; ++ nattrs = relForm->relnatts; ++ ++ switch (relkind) ++ { ++ case RELKIND_RELATION: ++ tclass = SEPG_CLASS_DB_TABLE; ++ ++ if (aclmask & ACL_SELECT) ++ required |= SEPG_DB_TABLE__SELECT; ++ if (aclmask & ACL_INSERT) ++ required |= SEPG_DB_TABLE__INSERT; ++ if (aclmask & ACL_UPDATE) ++ required |= (!modifiedCols ++ ? SEPG_DB_TABLE__LOCK ++ : SEPG_DB_TABLE__UPDATE); ++ if (aclmask & ACL_DELETE) ++ required |= SEPG_DB_TABLE__DELETE; ++ break; ++ ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ if (aclmask & ACL_SELECT) ++ required |= SEPG_DB_SEQUENCE__GET_VALUE; ++ break; ++ ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ if (aclmask != 0) ++ required |= SEPG_DB_VIEW__EXPAND; ++ break; ++ ++ default: ++ elog(ERROR, "Bug? unexpected relkind %c", relkind); ++ return false; ++ } ++ ++ if (required != 0) ++ rc = sepgsql_client_perms(rsid, tclass, required, auname, abort); ++ ++ ReleaseSysCache(tuple); ++ ++ if (!rc || relkind != RELKIND_RELATION) ++ return rc; ++ ++ /* ++ * Check column's permissions ++ */ ++ selColsEx = fixup_whole_row_reference(relOid, nattrs, ++ selectedCols); ++ modColsEx = fixup_whole_row_reference(relOid, nattrs, ++ modifiedCols); ++ columns = bms_union(selColsEx, modColsEx); ++ ++ while ((attno = bms_first_member(columns)) >= 0) ++ { ++ required = 0; ++ ++ if (bms_is_member(attno, selColsEx)) ++ required |= SEPG_DB_COLUMN__SELECT; ++ if (bms_is_member(attno, modColsEx)) ++ { ++ if (aclmask & ACL_UPDATE) ++ required |= SEPG_DB_COLUMN__UPDATE; ++ if (aclmask & ACL_INSERT) ++ required |= SEPG_DB_COLUMN__INSERT; ++ } ++ if (required == 0) ++ continue; ++ ++ attno += FirstLowInvalidHeapAttributeNumber; ++ rc = sepgsql_attribute_common(relOid, attno, required, abort); ++ if (!rc) ++ break; ++ } ++ ++ if (selColsEx != selectedCols) ++ bms_free(selColsEx); ++ if (modColsEx != modifiedCols) ++ bms_free(modColsEx); ++ bms_free(columns); ++ ++ return rc; ++ } ++#endif ++ return true; ++} ++ ++Oid * ++sepgsql_relation_create(const char *relName, ++ char relkind, ++ TupleDesc tupDesc, ++ Oid namespaceId, ++ List *supOids, ++ bool createAs) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ sepgsql_sid_t csid; ++ Oid *secLabels; ++ uint16 tclass; ++ uint32 perms; ++ AttrNumber index, attno, nitems; ++ ++ /* ++ * The secLabeld array stores security identifiers to be ++ * assigned on the new table and columns. ++ * ++ * secLabels[0] is security-id of the relation. ++ * secLabels[attnum - FirstLowInvalidHeapAttributeNumber] ++ * is security-id of the columns. ++ */ ++ secLabels = seclabelMakeRelationDefaults(tupDesc, supOids); ++ nitems = tupDesc->natts - FirstLowInvalidHeapAttributeNumber; ++ ++ switch (relkind) ++ { ++ case RELKIND_RELATION: ++ if (!OidIsValid(secLabels[0])) ++ { ++ tsid = sepgsql_get_default_table_secid(namespaceId); ++ secLabels[0] = tsid.secid; ++ } ++ tclass = SEPG_CLASS_DB_TABLE; ++ perms = SEPG_DB_TABLE__CREATE; ++ if (createAs) ++ perms |= SEPG_DB_TABLE__INSERT; ++ break; ++ ++ case RELKIND_SEQUENCE: ++ if (!OidIsValid(secLabels[0])) ++ { ++ tsid = sepgsql_get_default_sequence_secid(namespaceId); ++ secLabels[0] = tsid.secid; ++ } ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ perms = SEPG_DB_SEQUENCE__CREATE; ++ break; ++ ++ case RELKIND_VIEW: ++ if (!OidIsValid(secLabels[0])) ++ { ++ tsid = sepgsql_get_default_view_secid(namespaceId); ++ secLabels[0] = tsid.secid; ++ } ++ tclass = SEPG_CLASS_DB_VIEW; ++ perms = SEPG_DB_VIEW__CREATE; ++ break; ++ ++ case RELKIND_COMPOSITE_TYPE: ++ if (!OidIsValid(secLabels[0])) ++ { ++ tsid = sepgsql_get_default_tuple_secid(TypeRelationId); ++ secLabels[0] = seclabelMoveSecid(RelationRelationId, ++ TypeRelationId, tsid.secid); ++ } ++ tclass = SEPG_CLASS_DB_TUPLE; ++ perms = SEPG_DB_TUPLE__INSERT; ++ break; ++ ++ default: ++ elog(ERROR, "Bug? unexpected relkind %c", relkind); ++ return NULL; ++ } ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_table:{create} or others */ ++ tsid.relid = RelationRelationId; ++ tsid.secid = secLabels[0]; ++ sepgsql_client_perms(tsid, tclass, perms, relName, true); ++ ++ /* no individual security-id except for RELKIND_RELATION */ ++ if (relkind != RELKIND_RELATION) ++ { ++ Oid securityId = seclabelMoveSecid(AttributeRelationId, ++ RelationRelationId, secLabels[0]); ++ ++ for (index = 1; index < nitems; index++) ++ secLabels[index] = securityId; ++ ++ return secLabels; ++ } ++ ++ /* ++ * security context of the columns ++ */ ++ for (index = 1; index < nitems; index++) ++ { ++ Form_pg_attribute attForm; ++ char auname[NAMEDATALEN * 2 + 10]; ++ ++ attno = index + FirstLowInvalidHeapAttributeNumber; ++ ++ /* skip unnecessary system columns */ ++ if ((attno == ObjectIdAttributeNumber && !tupDesc->tdhasoid) || ++ (attno == SecurityLabelAttributeNumber && !tupDesc->tdhassecid)) ++ continue; ++ ++ if (!OidIsValid(secLabels[index])) ++ { ++ csid = sepgsql_client_create_secid(tsid, ++ SEPG_CLASS_DB_COLUMN, ++ AttributeRelationId); ++ secLabels[index] = csid.secid; ++ } ++ ++ if (attno < 0) ++ attForm = SystemAttributeDefinition(attno, ++ tupDesc->tdhasoid, ++ tupDesc->tdhassecid); ++ else ++ attForm = tupDesc->attrs[attno]; ++ ++ /* db_column:{create (insert)} permission */ ++ csid.relid = AttributeRelationId; ++ csid.secid = secLabels[index]; ++ ++ perms = SEPG_DB_COLUMN__CREATE; ++ if (createAs && attno >= 0) ++ perms |= SEPG_DB_COLUMN__INSERT; ++ ++ snprintf(auname, sizeof(auname), "%s.%s", ++ relName, NameStr(attForm->attname)); ++ ++ sepgsql_client_perms(csid, ++ SEPG_CLASS_DB_COLUMN, ++ perms, ++ auname, ++ true); ++ } ++ return secLabels; ++ } ++#endif ++ return NULL; ++} ++ ++void ++sepgsql_relation_alter(Oid relationOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_alter_schema(Oid relationOid, Oid newSchema) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_alter_rename(Oid relationOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_alter_inherit(Oid childOid, Oid parentOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(childOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_relation_relabel(Oid relationOid, char *new_label) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char *auname; ++ char relkind; ++ uint16 tclass = 0; ++ ++ nsid.relid = RelationRelationId; ++ nsid.secid = seclabelTransInput(nsid.relid, new_label); ++ ++ auname = get_rel_name(relationOid); ++ relkind = get_rel_relkind(relationOid); ++ ++ switch (relkind) ++ { ++ case RELKIND_RELATION: ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ break; ++ ++ case RELKIND_COMPOSITE_TYPE: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ ++ default: ++ elog(ERROR, "unexpected relkind %c", relkind); ++ break;; ++ } ++ /* db_xxx:{setattr relabelfrom} */ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR | ++ SEPG_DB_TABLE__RELABELFROM, true); ++ ++ /* db_xxx:{relabelto} */ ++ sepgsql_client_perms(nsid, ++ tclass, ++ SEPG_DB_TABLE__RELABELTO, ++ auname, true); ++ pfree(auname); ++ ++ return nsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_relation_drop(Oid relationOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__DROP, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_getattr(Oid relationOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__GETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_grant(Oid relationOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_comment(Oid relationOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++bool ++sepgsql_relation_cluster(Oid relationOid, bool abort) ++{ ++#ifdef HAVE_SELINUX ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ ++ if (sepgsql_is_enabled()) ++ { ++ bool retval = ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__INDEXON, true); ++ return retval; ++ } ++#endif ++ return true; ++} ++ ++void ++sepgsql_relation_truncate(Relation rel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); ++ ++ /* db_table:{delete} */ ++ sepgsql_relation_common(RelationGetRelid(rel), ++ SEPG_DB_TABLE__DELETE, true); ++ /* db_tuple:{delete} */ ++ } ++#endif ++} ++ ++void ++sepgsql_relation_lock(Relation rel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); ++ ++ /* db_table:{lock} */ ++ sepgsql_relation_common(RelationGetRelid(rel), ++ SEPG_DB_TABLE__LOCK, true); ++ } ++#endif ++} ++ ++void ++sepgsql_relation_reindex(Oid relationOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ ++ /* db_table:{indexon} */ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__INDEXON, true); ++ } ++#endif ++} ++ ++void ++sepgsql_view_replace(Oid viewOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(viewOid) == RELKIND_VIEW); ++ /* db_view:{setattr} */ ++ sepgsql_relation_common(viewOid, ++ SEPG_DB_VIEW__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_index_create(Oid relationOid, Oid namespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__ADD_NAME, true); ++ /* db_table:{setattr indexon} */ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR | ++ SEPG_DB_TABLE__INDEXON, true); ++ } ++#endif ++} ++ ++void ++sepgsql_index_reindex(Oid indexOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ HeapTuple tuple; ++ Oid relationOid; ++ ++ tuple = SearchSysCache1(INDEXRELID, indexOid); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for index %u", indexOid); ++ relationOid = ((Form_pg_index) GETSTRUCT(tuple))->indrelid; ++ ReleaseSysCache(tuple); ++ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__INDEXON, true); ++ } ++#endif ++} ++ ++void ++sepgsql_sequence_get_value(Oid sequenceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE); ++ sepgsql_relation_common(sequenceOid, ++ SEPG_DB_SEQUENCE__GET_VALUE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_sequence_next_value(Oid sequenceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE); ++ sepgsql_relation_common(sequenceOid, ++ SEPG_DB_SEQUENCE__NEXT_VALUE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_sequence_set_value(Oid sequenceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE); ++ sepgsql_relation_common(sequenceOid, ++ SEPG_DB_SEQUENCE__SET_VALUE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_rule_create(Oid relationOid, const char *ruleName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_rule_drop(Oid relationOid, const char *ruleName, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled() && !cascade) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_rule_comment(Oid relationOid, const char *ruleName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_trigger_create(Oid relationOid, const char *triggerName, ++ Oid constrrelid, Oid funcOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ ++ /* db_table:{setattr} */ ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ /* db_procedure:{install} */ ++ // sepgsql_procedure_common... ++ } ++#endif ++} ++ ++void ++sepgsql_trigger_alter(Oid relationOid, const char *triggerName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_trigger_drop(Oid relationOid, const char *triggerName, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled() && !cascade) ++ { ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_trigger_comment(Oid relationOid, const char *triggerName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} ++ ++void ++sepgsql_constraint_comment(Oid relationOid, const char *constName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); ++ sepgsql_relation_common(relationOid, ++ SEPG_DB_TABLE__SETATTR, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/role.c b/src/backend/sepgsql/role.c +new file mode 100644 +index 0000000..3c4891c +--- /dev/null ++++ b/src/backend/sepgsql/role.c +@@ -0,0 +1,142 @@ ++/* ++ * role.c ++ * ++ * SELinux hooks related to roles ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_authid.h" ++#include "catalog/pg_seclabel.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_role_common(Oid roleOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for role %u", roleOid); ++ ++ tsid.relid = AuthIdRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_role_create(const char *roleName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ nsid = sepgsql_get_default_tuple_secid(AuthIdRelationId); ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ roleName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_role_alter(Oid roleOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_role_relabel(Oid roleOid, char *newLabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ ++ tsid.relid = AuthIdRelationId; ++ tsid.secid = seclabelTransInput(tsid.relid, newLabel); ++ ++ /* db_tuple:{update relabelfrom} */ ++ sepgsql_role_common(roleOid, ++ SEPG_DB_TUPLE__UPDATE | ++ SEPG_DB_TUPLE__RELABELFROM, ++ true); ++ ++ /* db_tuple:{relabelto} */ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELTO, ++ GetUserNameFromId(roleOid), ++ true); ++ return tsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_role_drop(Oid roleOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_role_grant(Oid roleOid, bool is_grant, List *memberIds) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_role_comment(Oid roleOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/rowlv.c b/src/backend/sepgsql/rowlv.c +new file mode 100644 +index 0000000..1078777 +--- /dev/null ++++ b/src/backend/sepgsql/rowlv.c +@@ -0,0 +1,346 @@ ++/* ++ * rowlv.c ++ * ++ * Row-level access control facilities ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/heapam.h" ++#include "access/sysattr.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_class.h" ++#include "catalog/pg_database.h" ++#include "catalog/pg_language.h" ++#include "catalog/pg_largeobject_metadata.h" ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_seclabel.h" ++#include "catalog/pg_type.h" ++#include "nodes/makefuncs.h" ++#include "parser/parsetree.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "storage/bufmgr.h" ++#include "utils/fmgroids.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++#include "utils/tqual.h" ++ ++static int sepgsql_rowlv_mode = SEPGSQL_ROWLV_FILTER; ++ ++int ++sepgsql_rowlv_get_mode(void) ++{ ++ return sepgsql_rowlv_mode; ++} ++ ++int ++sepgsql_rowlv_set_mode(int new_mode) ++{ ++ int old_mode = sepgsql_rowlv_mode; ++ ++ Assert(new_mode == SEPGSQL_ROWLV_FILTER || ++ new_mode == SEPGSQL_ROWLV_ABORT || ++ new_mode == SEPGSQL_ROWLV_BYPASS); ++ ++ sepgsql_rowlv_mode = new_mode; ++ ++ return old_mode; ++} ++ ++uint32 ++sepgsql_rowlv_permissions(RangeTblEntry *rte) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid relOid = rte->relid; ++ HeapTuple reltup; ++ bool relhassecids; ++ uint32 required = 0; ++ ++ if (!OidIsValid(relOid)) ++ return 0; ++ ++ if (get_rel_relkind(relOid) != RELKIND_RELATION) ++ return 0; ++ ++ reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); ++ if (!HeapTupleIsValid(reltup)) ++ elog(ERROR, "cache lookup failed for relation %u", relOid); ++ relhassecids = ((Form_pg_class) GETSTRUCT(reltup))->relhassecids; ++ ReleaseSysCache(reltup); ++ ++ if (!relhassecids) ++ return 0; ++ ++ if (rte->requiredPerms & ACL_SELECT) ++ required |= SEPG_DB_TUPLE__SELECT; ++ ++ if (rte->requiredPerms & ACL_UPDATE && ++ !bms_is_empty(rte->modifiedCols)) ++ required |= SEPG_DB_TUPLE__UPDATE; ++ ++ if (rte->requiredPerms & ACL_DELETE) ++ required |= SEPG_DB_TUPLE__DELETE; ++ ++ return required; ++ } ++#endif ++ return 0; ++} ++ ++#ifdef HAVE_SELINUX ++static bool ++sepgsql_tuple_perms(Relation rel, HeapTuple tuple, uint32 required, bool abort) ++{ ++ Form_pg_class relForm; ++ Form_pg_attribute attForm; ++ Form_pg_type typForm; ++ sepgsql_sid_t tsid; ++ uint16 tclass; ++ bool result; ++ ++ /* object class? */ ++ switch (RelationGetRelid(rel)) ++ { ++ case DatabaseRelationId: ++ tclass = SEPG_CLASS_DB_DATABASE; ++ break; ++ ++ case NamespaceRelationId: ++ tclass = SEPG_CLASS_DB_SCHEMA; ++ break; ++ ++ case RelationRelationId: ++ relForm = (Form_pg_class) GETSTRUCT(tuple); ++ switch (relForm->relkind) ++ { ++ case RELKIND_RELATION: ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ break; ++ case RELKIND_COMPOSITE_TYPE: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ default: /* index, toast */ ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ } ++ break; ++ ++ case AttributeRelationId: ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ switch (get_rel_relkind(attForm->attrelid)) ++ { ++ case RELKIND_RELATION: ++ tclass = SEPG_CLASS_DB_COLUMN; ++ break; ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ break; ++ case RELKIND_COMPOSITE_TYPE: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ default: /* index, toast */ ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ } ++ break; ++ ++ case ProcedureRelationId: ++ tclass = SEPG_CLASS_DB_PROCEDURE; ++ break; ++ ++ case LanguageRelationId: ++ tclass = SEPG_CLASS_DB_LANGUAGE; ++ break; ++ ++ case LargeObjectMetadataRelationId: ++ tclass = SEPG_CLASS_DB_BLOB; ++ break; ++ ++ case TypeRelationId: ++ typForm = (Form_pg_type) GETSTRUCT(tuple); ++ if (!OidIsValid(typForm->typrelid)) ++ tclass = SEPG_CLASS_DB_TUPLE; ++ else ++ { ++ switch (get_rel_relkind(typForm->typrelid)) ++ { ++ case RELKIND_RELATION: ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ case RELKIND_SEQUENCE: ++ tclass = SEPG_CLASS_DB_SEQUENCE; ++ break; ++ case RELKIND_VIEW: ++ tclass = SEPG_CLASS_DB_VIEW; ++ break; ++ case RELKIND_COMPOSITE_TYPE: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ default: /* index, toast */ ++ tclass = SEPG_CLASS_DB_TABLE; ++ break; ++ } ++ } ++ break; ++ ++ default: ++ tclass = SEPG_CLASS_DB_TUPLE; ++ break; ++ } ++ ++ /* do permission check */ ++ tsid.relid = RelationGetRelid(rel); ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ result = sepgsql_client_perms(tsid, tclass, required, NULL, abort); ++ ++ if (!result) ++ elog(NOTICE, "denied: oid=%u tsid(relid=%u, secid=%u) label=%s tclass=%d required=%u", HeapTupleGetOid(tuple), tsid.relid, tsid.secid, seclabelRawOutput(tsid.relid, tsid.secid), tclass, required); ++ ++ return result; ++} ++#endif ++ ++bool ++sepgsql_rowlv_quals(Relation rel, uint32 required, ++ TupleTableSlot *slot, bool abort) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ HeapTuple tuple; ++ ++ /* no permission to be checked */ ++ if (!required) ++ return true; ++ ++ /* mismatch invocation for the current rowlv mode */ ++ if ((!abort && sepgsql_rowlv_mode != SEPGSQL_ROWLV_FILTER) || ++ (abort && sepgsql_rowlv_mode != SEPGSQL_ROWLV_ABORT)) ++ return true; ++ ++ Assert(OidIsValid(RelationGetRelid(rel))); ++ ++ tuple = ExecMaterializeSlot(slot); ++ ++ return sepgsql_tuple_perms(rel, tuple, required, abort); ++ } ++#endif ++ return true; ++} ++ ++bool ++sepgsql_rowlv_copyto(Relation rel, HeapTuple tuple) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ return sepgsql_tuple_perms(rel, tuple, SEPG_DB_TUPLE__SELECT, false); ++ } ++#endif ++ return true; ++} ++ ++void ++sepgsql_tuple_insert(Relation rel, HeapTuple tuple) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ Oid relOid = RelationGetRelid(rel); ++ ++ if (!RelationGetForm(rel)->relhassecids) ++ { ++ nsid.relid = RelationRelationId; ++ nsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); ++ } ++ else if (OidIsValid(HeapTupleGetSecid(tuple))) ++ { ++ nsid.relid = relOid; ++ nsid.secid = HeapTupleGetSecid(tuple); ++ } ++ else ++ { ++ nsid = sepgsql_get_default_tuple_secid(relOid); ++ HeapTupleSetSecid(tuple, nsid.secid); ++ } ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ NULL, ++ true); ++ } ++#endif ++} ++ ++void ++sepgsql_tuple_update(Relation rel, ItemPointer otid, HeapTuple newtup) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ HeapTupleData oldtup; ++ Buffer oldbuf; ++ Oid newSecId = HeapTupleGetSecid(newtup); ++ Oid oldSecId; ++ ++ /* ++ * heap_update() preserves security id of the original tuple, ++ * if no explicit security label was given, so we don't need ++ * to check anything. ++ * At this point, db_tuple:{update} is already checked. ++ */ ++ if (!OidIsValid(newSecId)) ++ return; ++ ++ /* ++ * User gave an explicit security label ++ */ ++ ItemPointerCopy(otid, &oldtup.t_self); ++ if (!heap_fetch(rel, SnapshotAny, &oldtup, &oldbuf, false, NULL)) ++ elog(ERROR, "failed to fetch old version of the tuple"); ++ ++ tsid.relid = RelationGetRelid(rel); ++ oldSecId = HeapTupleGetSecid(&oldtup); ++ ++ if (!seclabelCompareSecid(tsid.relid, oldSecId, ++ tsid.relid, newSecId)) ++ { ++ /* db_tuple:{relabelfrom} */ ++ tsid.secid = oldSecId; ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELFROM, ++ NULL, ++ true); ++ ++ /* db_tuple:{relabelto} */ ++ tsid.secid = newSecId; ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELTO, ++ NULL, ++ true); ++ } ++ ReleaseBuffer(oldbuf); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/schema.c b/src/backend/sepgsql/schema.c +new file mode 100644 +index 0000000..24878f9 +--- /dev/null ++++ b/src/backend/sepgsql/schema.c +@@ -0,0 +1,173 @@ ++/* ++ * schema.c ++ * ++ * SELinux hooks related to schema ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_seclabel.h" ++#include "miscadmin.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_schema_common(Oid namespaceOid, uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(NAMESPACEOID, ++ ObjectIdGetDatum(namespaceOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for namespace %u", namespaceOid); ++ ++ tsid.relid = NamespaceRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_SCHEMA, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++#endif ++ ++Oid ++sepgsql_schema_create(const char *nspName, bool is_temp) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ /* compute default security context */ ++ nsid = sepgsql_get_default_schema_secid(MyDatabaseId); ++ ++ /* db_schema:{create} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_SCHEMA, ++ SEPG_DB_SCHEMA__CREATE, ++ nspName, true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_schema_alter(Oid namespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__SETATTR, ++ true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_schema_relabel(Oid namespaceOid, char *new_label) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ char *auname; ++ ++ nsid.relid = NamespaceRelationId; ++ nsid.secid = seclabelTransInput(nsid.relid, new_label); ++ ++ auname = get_namespace_name(namespaceOid); ++ ++ /* db_schema:{setattr relabelfrom} */ ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__SETATTR | ++ SEPG_DB_SCHEMA__RELABELFROM, ++ true); ++ ++ /* db_schema:{relabelto} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_SCHEMA, ++ SEPG_DB_SCHEMA__RELABELTO, ++ auname, ++ true); ++ pfree(auname); ++ ++ return nsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_schema_drop(Oid namespaceOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__DROP, ++ true); ++ } ++#endif ++} ++ ++void ++sepgsql_schema_grant(Oid namespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__SETATTR, ++ true); ++ } ++#endif ++} ++ ++bool ++sepgsql_schema_search(Oid namespaceOid, bool abort) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ bool retval = ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__SEARCH, ++ abort); ++ return retval; ++ } ++#endif ++ return true; ++} ++ ++void ++sepgsql_schema_comment(Oid namespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_schema_common(namespaceOid, ++ SEPG_DB_SCHEMA__SETATTR, ++ true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/selinux.c b/src/backend/sepgsql/selinux.c +new file mode 100644 +index 0000000..5e40cf9 +--- /dev/null ++++ b/src/backend/sepgsql/selinux.c +@@ -0,0 +1,690 @@ ++/* ++ * src/backend/security/sepgsql/selinux.c ++ * Routines to communicate with SELinux. ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_seclabel.h" ++#include "libpq/libpq.h" ++#include "miscadmin.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/guc.h" ++#include "utils/memutils.h" ++ ++#include ++ ++/* ++ * selinux_catalog ++ * ++ * This static translation lookup table enables to associate a certain ++ * object class/permission name with its internal code, such as ++ * SEPG_CLASS_DB_SCHEMA. ++ * ++ * SELinux requires applications to represent object class and a set of ++ * permissions in code, instead of its name, when we ask SELinux's decision. ++ * ++ * See the definition of security_compute_av(3) API in libselinux. ++ * We need to gives a code of object class, and interpret what permissions ++ * are allowed on the object class from av_decision structure. ++ * Actual values of the code depend on the security policy. In other words, ++ * we cannot know what number is assigned on a certain object class and ++ * permissions. ++ * The string_to_security_class(3) and string_to_av_perm(3) APIs takes ++ * arguments with the name of object class/permission, and returns the ++ * code for the given object class/permissions. ++ * For example, we can know what code is assigned on the "db_table" class ++ * using these functions as follows: ++ * ++ * uint16 tclass_ex = string_to_security_class("db_table"); ++ * ++ * On the other hand, we use an alternative code internally to simplify ++ * the implementation, such as SEPG_CLASS_* for object class. ++ * The following selinux_catalog is used to translate the 'internal' ++ * code and the 'external' code. ++ * ++ * It allows to lookup name of the object class or permission corresponding ++ * to a certain 'internal' code. Then, we can give the name to SELinux's ++ * API to obtain 'external' code which can be used to ask in-kernel SELinux. ++ */ ++static struct ++{ ++ const char *class_name; ++ uint16 class_code; ++ struct ++ { ++ const char *perm_name; ++ uint32 perm_code; ++ } perms[32]; ++} selinux_catalog[] = { ++ { ++ "process", SEPG_CLASS_PROCESS, ++ { ++ { "translation", SEPG_PROCESS__TRANSITION }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "file", SEPG_CLASS_FILE, ++ { ++ { "read", SEPG_FILE__READ }, ++ { "write", SEPG_FILE__WRITE }, ++ { "create", SEPG_FILE__CREATE }, ++ { "getattr", SEPG_FILE__GETATTR }, ++ { "unlink", SEPG_FILE__UNLINK }, ++ { "rename", SEPG_FILE__RENAME }, ++ { "append", SEPG_FILE__APPEND }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "dir", SEPG_CLASS_DIR, ++ { ++ { "read", SEPG_DIR__READ }, ++ { "write", SEPG_DIR__WRITE }, ++ { "create", SEPG_DIR__CREATE }, ++ { "getattr", SEPG_DIR__GETATTR }, ++ { "unlink", SEPG_DIR__UNLINK }, ++ { "rename", SEPG_DIR__RENAME }, ++ { "search", SEPG_DIR__SEARCH }, ++ { "add_name", SEPG_DIR__ADD_NAME }, ++ { "remove_name", SEPG_DIR__REMOVE_NAME }, ++ { "rmdir", SEPG_DIR__RMDIR }, ++ { "reparent", SEPG_DIR__REPARENT }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "lnk_file", SEPG_CLASS_LNK_FILE, ++ { ++ { "read", SEPG_LNK_FILE__READ }, ++ { "write", SEPG_LNK_FILE__WRITE }, ++ { "create", SEPG_LNK_FILE__CREATE }, ++ { "getattr", SEPG_LNK_FILE__GETATTR }, ++ { "unlink", SEPG_LNK_FILE__UNLINK }, ++ { "rename", SEPG_LNK_FILE__RENAME }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "chr_file", SEPG_CLASS_CHR_FILE, ++ { ++ { "read", SEPG_CHR_FILE__READ }, ++ { "write", SEPG_CHR_FILE__WRITE }, ++ { "create", SEPG_CHR_FILE__CREATE }, ++ { "getattr", SEPG_CHR_FILE__GETATTR }, ++ { "unlink", SEPG_CHR_FILE__UNLINK }, ++ { "rename", SEPG_CHR_FILE__RENAME }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "blk_file", SEPG_CLASS_BLK_FILE, ++ { ++ { "read", SEPG_BLK_FILE__READ }, ++ { "write", SEPG_BLK_FILE__WRITE }, ++ { "create", SEPG_BLK_FILE__CREATE }, ++ { "getattr", SEPG_BLK_FILE__GETATTR }, ++ { "unlink", SEPG_BLK_FILE__UNLINK }, ++ { "rename", SEPG_BLK_FILE__RENAME }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "sock_file", SEPG_CLASS_SOCK_FILE, ++ { ++ { "read", SEPG_SOCK_FILE__READ }, ++ { "write", SEPG_SOCK_FILE__WRITE }, ++ { "create", SEPG_SOCK_FILE__CREATE }, ++ { "getattr", SEPG_SOCK_FILE__GETATTR }, ++ { "unlink", SEPG_SOCK_FILE__UNLINK }, ++ { "rename", SEPG_SOCK_FILE__RENAME }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "fifo_file", SEPG_CLASS_FIFO_FILE, ++ { ++ { "read", SEPG_FIFO_FILE__READ }, ++ { "write", SEPG_FIFO_FILE__WRITE }, ++ { "create", SEPG_FIFO_FILE__CREATE }, ++ { "getattr", SEPG_FIFO_FILE__GETATTR }, ++ { "unlink", SEPG_FIFO_FILE__UNLINK }, ++ { "rename", SEPG_FIFO_FILE__RENAME }, ++ { NULL, 0UL } ++ } ++ }, ++ { ++ "db_database", SEPG_CLASS_DB_DATABASE, ++ { ++ { "create", SEPG_DB_DATABASE__CREATE }, ++ { "drop", SEPG_DB_DATABASE__DROP }, ++ { "getattr", SEPG_DB_DATABASE__GETATTR }, ++ { "setattr", SEPG_DB_DATABASE__SETATTR }, ++ { "relabelfrom", SEPG_DB_DATABASE__RELABELFROM }, ++ { "relabelto", SEPG_DB_DATABASE__RELABELTO }, ++ { "access", SEPG_DB_DATABASE__ACCESS }, ++ { "load_module", SEPG_DB_DATABASE__LOAD_MODULE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_schema", SEPG_CLASS_DB_SCHEMA, ++ { ++ { "create", SEPG_DB_SCHEMA__CREATE }, ++ { "drop", SEPG_DB_SCHEMA__DROP }, ++ { "getattr", SEPG_DB_SCHEMA__GETATTR }, ++ { "setattr", SEPG_DB_SCHEMA__SETATTR }, ++ { "relabelfrom", SEPG_DB_SCHEMA__RELABELFROM }, ++ { "relabelto", SEPG_DB_SCHEMA__RELABELTO }, ++ { "search", SEPG_DB_SCHEMA__SEARCH }, ++ { "add_name", SEPG_DB_SCHEMA__ADD_NAME }, ++ { "remove_name", SEPG_DB_SCHEMA__REMOVE_NAME }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_table", SEPG_CLASS_DB_TABLE, ++ { ++ { "create", SEPG_DB_TABLE__CREATE }, ++ { "drop", SEPG_DB_TABLE__DROP }, ++ { "getattr", SEPG_DB_TABLE__GETATTR }, ++ { "setattr", SEPG_DB_TABLE__SETATTR }, ++ { "relabelfrom", SEPG_DB_TABLE__RELABELFROM }, ++ { "relabelto", SEPG_DB_TABLE__RELABELTO }, ++ { "select", SEPG_DB_TABLE__SELECT }, ++ { "update", SEPG_DB_TABLE__UPDATE }, ++ { "insert", SEPG_DB_TABLE__INSERT }, ++ { "delete", SEPG_DB_TABLE__DELETE }, ++ { "lock", SEPG_DB_TABLE__LOCK }, ++ { "indexon", SEPG_DB_TABLE__INDEXON }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_view", SEPG_CLASS_DB_VIEW, ++ { ++ { "create", SEPG_DB_VIEW__CREATE }, ++ { "drop", SEPG_DB_VIEW__DROP }, ++ { "getattr", SEPG_DB_VIEW__GETATTR }, ++ { "setattr", SEPG_DB_VIEW__SETATTR }, ++ { "relabelfrom", SEPG_DB_VIEW__RELABELFROM }, ++ { "relabelto", SEPG_DB_VIEW__RELABELTO }, ++ { "expand", SEPG_DB_VIEW__EXPAND }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_sequence", SEPG_CLASS_DB_SEQUENCE, ++ { ++ { "create", SEPG_DB_SEQUENCE__CREATE }, ++ { "drop", SEPG_DB_SEQUENCE__DROP }, ++ { "getattr", SEPG_DB_SEQUENCE__GETATTR }, ++ { "setattr", SEPG_DB_SEQUENCE__SETATTR }, ++ { "relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM }, ++ { "relabelto", SEPG_DB_SEQUENCE__RELABELTO }, ++ { "get_value", SEPG_DB_SEQUENCE__GET_VALUE }, ++ { "next_value", SEPG_DB_SEQUENCE__NEXT_VALUE }, ++ { "set_value", SEPG_DB_SEQUENCE__SET_VALUE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_procedure", SEPG_CLASS_DB_PROCEDURE, ++ { ++ { "create", SEPG_DB_PROCEDURE__CREATE }, ++ { "drop", SEPG_DB_PROCEDURE__DROP }, ++ { "getattr", SEPG_DB_PROCEDURE__GETATTR }, ++ { "setattr", SEPG_DB_PROCEDURE__SETATTR }, ++ { "relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM }, ++ { "relabelto", SEPG_DB_PROCEDURE__RELABELTO }, ++ { "execute", SEPG_DB_PROCEDURE__EXECUTE }, ++ { "entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT }, ++ { "install", SEPG_DB_PROCEDURE__INSTALL }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_column", SEPG_CLASS_DB_COLUMN, ++ { ++ { "create", SEPG_DB_COLUMN__CREATE }, ++ { "drop", SEPG_DB_COLUMN__DROP }, ++ { "getattr", SEPG_DB_COLUMN__GETATTR }, ++ { "setattr", SEPG_DB_COLUMN__SETATTR }, ++ { "relabelfrom", SEPG_DB_COLUMN__RELABELFROM }, ++ { "relabelto", SEPG_DB_COLUMN__RELABELTO }, ++ { "select", SEPG_DB_COLUMN__SELECT }, ++ { "update", SEPG_DB_COLUMN__UPDATE }, ++ { "insert", SEPG_DB_COLUMN__INSERT }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_tuple", SEPG_CLASS_DB_TUPLE, ++ { ++ { "relabelfrom", SEPG_DB_TUPLE__RELABELFROM }, ++ { "relabelto", SEPG_DB_TUPLE__RELABELTO }, ++ { "select", SEPG_DB_TUPLE__SELECT }, ++ { "update", SEPG_DB_TUPLE__UPDATE }, ++ { "insert", SEPG_DB_TUPLE__INSERT }, ++ { "delete", SEPG_DB_TUPLE__DELETE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_blob", SEPG_CLASS_DB_BLOB, ++ { ++ { "create", SEPG_DB_BLOB__CREATE }, ++ { "drop", SEPG_DB_BLOB__DROP }, ++ { "getattr", SEPG_DB_BLOB__GETATTR }, ++ { "setattr", SEPG_DB_BLOB__SETATTR }, ++ { "relabelfrom", SEPG_DB_BLOB__RELABELFROM }, ++ { "relabelto", SEPG_DB_BLOB__RELABELTO }, ++ { "read", SEPG_DB_BLOB__READ }, ++ { "write", SEPG_DB_BLOB__WRITE }, ++ { "import", SEPG_DB_BLOB__IMPORT }, ++ { "export", SEPG_DB_BLOB__EXPORT }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_language", SEPG_CLASS_DB_LANGUAGE, ++ { ++ { "create", SEPG_DB_LANGUAGE__CREATE }, ++ { "drop", SEPG_DB_LANGUAGE__DROP }, ++ { "getattr", SEPG_DB_LANGUAGE__GETATTR }, ++ { "setattr", SEPG_DB_LANGUAGE__SETATTR }, ++ { "relabelfrom", SEPG_DB_LANGUAGE__RELABELFROM }, ++ { "relabelto", SEPG_DB_LANGUAGE__RELABELTO }, ++ { "implement", SEPG_DB_LANGUAGE__IMPLEMENTE }, ++ { "execute", SEPG_DB_LANGUAGE__EXECUTE }, ++ { NULL, 0UL }, ++ } ++ }, ++}; ++ ++/* ++ * GUC option: sepostgresql = [default|enforcing|permissive|disabled] ++ * ++ * SEPGSQL_MODE_DEFAULT : It follows system setting ++ * SEPGSQL_MODE_ENFORCING : Use enforcing mode always ++ * SEPGSQL_MODE_PERMISSIVE : Use permissive mode always ++ * SEPGSQL_MODE_INTERNAL : Internally used mode. Same as permissive mode ++ * except for silence in audit logs ++ * SEPGSQL_MODE_DISABLED : It always disables SE-PgSQL configuration ++ */ ++int sepostgresql_mode; ++ ++/* ++ * sepgsql_is_enabled ++ * ++ * If it returns true, SE-PgSQL is enabled. Otherwise, it is disabled. ++ */ ++bool ++sepgsql_is_enabled(void) ++{ ++ static int enabled = -1; ++ ++ /* ++ * If sepostgresql = disabled, it always returns FALSE ++ * independently from the system status. ++ */ ++ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED) ++ return false; ++ ++ /* ++ * SE-PgSQL needs SELinux is enabled on the operating system. ++ * If it is disabled, SE-PgSQL has to be also disabled, even if ++ * 'enforcing' or 'permissive' are specified. ++ */ ++ if (enabled < 0) ++ enabled = is_selinux_enabled(); ++ ++ return enabled > 0 ? true : false; ++} ++ ++/* ++ * sepgsql_get_enforce ++ * ++ * It returns true, if SE-PgSQL performs in enforcing mode. ++ * ++ * In enforcing mode, SE-PgSQL performs as expected. It checks permissions ++ * on the required action, and it prevents them if violated. ++ * In permissive mode, SE-PgSQL also checks permissions, but it does not ++ * prevent anything, even if violated. It generates audit logs for access ++ * violations, so we can use this mode to debug security policy itself. ++ */ ++bool ++sepgsql_get_enforce(void) ++{ ++ if (sepostgresql_mode == SEPGSQL_MODE_DEFAULT) ++ { ++ if (security_getenforce() == 1) ++ return true; ++ } ++ else if (sepostgresql_mode == SEPGSQL_MODE_ENFORCING) ++ return true; ++ ++ return false; ++} ++ ++/* ++ * sepgsql_show_mode ++ * ++ * It returns the current performing mode ('selinux_support') ++ * in human readable form. ++ */ ++const char * ++sepgsql_show_mode(void) ++{ ++ if (!sepgsql_is_enabled()) ++ return "disabled"; ++ ++ if (!sepgsql_get_enforce()) ++ return "permissive"; ++ ++ return "enforcing"; ++} ++ ++/* ++ * GUC parameter to turn on/off debuging audit generation ++ */ ++bool sepgsql_debug_audit; ++ ++/* ++ * sepgsql_audit_log ++ * ++ * It generates a security audit record. In the default, it writes out ++ * audit records into standard PG's logfile. It also allows to set up ++ * external audit log receiver, such as auditd in Linux, using the ++ * sepgsql_audit_hook. ++ * ++ * SELinux can control what should be audited and should not using ++ * "auditdeny" and "auditallow" rules in the security policy. In the ++ * default, all the access violations are audited, and all the access ++ * allowed are not audited. But we can set up the security policy, so ++ * we can have exceptions. So, it is necessary to follow the suggestion ++ * come from the security policy. (av_decision.auditallow and auditdeny) ++ * ++ * Security audit is an important feature, because it enables us to check ++ * what was happen if we have a security incident. In fact, ISO/IEC15408 ++ * defines several security functionalities for audit features. ++ */ ++void ++sepgsql_audit_log(bool denied, char *scontext, char *tcontext, ++ uint16 tclass, uint32 audited, const char *audit_name) ++{ ++ StringInfoData buf; ++ const char *tclass_name; ++ const char *perm_name; ++ int level = LOG; ++ int i; ++ ++ /* ++ * translation of security contexts to human readable format, ++ * if sepgsql_mcstrans is turned on. ++ */ ++ scontext = sepgsql_mcstrans_out(scontext); ++ tcontext = sepgsql_mcstrans_out(tcontext); ++ ++ /* lookup name of the object class */ ++ tclass_name = selinux_catalog[tclass].class_name; ++ ++ /* lookup name of the permissions */ ++ initStringInfo(&buf); ++ appendStringInfo(&buf, "{"); ++ ++ for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++) ++ { ++ if (audited & (1UL << i)) ++ { ++ perm_name = selinux_catalog[tclass].perms[i].perm_name; ++ appendStringInfo(&buf, " %s", perm_name); ++ } ++ } ++ appendStringInfo(&buf, " }"); ++ ++ /* ++ * Call external audit module, if loaded ++ */ ++ appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, tclass_name); ++ if (audit_name) ++ appendStringInfo(&buf, " name=%s", audit_name); ++ ++ if (sepgsql_debug_audit) ++ level = client_min_messages; ++ ++ ereport(level, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("SELinux: %s %s", ++ (denied ? "denied" : "allowed"), buf.data))); ++} ++ ++/* ++ * sepgsql_compute_avd ++ * ++ * It actually asks SELinux what permissions are allowed on a pair of ++ * the security contexts and object class. It also returns what permissions ++ * should be audited on access violation or allowed. ++ * In most cases, subject's security context (scontext) is a client, and ++ * target security context (tcontext) is a database object. ++ * ++ * The access control decision shall be set on the given av_decision. ++ * The av_decision.allowed has a bitmask of SEPG___ ++ * to suggest a set of allowed actions in this object class. ++ */ ++void ++sepgsql_compute_avd(char *scontext, char *tcontext, ++ uint16 tclass, struct av_decision *avd) ++{ ++ const char *tclass_name; ++ security_class_t tclass_ex; ++ struct av_decision avd_ex; ++ int i, deny_unknown = security_deny_unknown(); ++ ++ /* Get external code of the object class*/ ++ Assert(tclass < SEPG_CLASS_MAX); ++ ++ tclass_name = selinux_catalog[tclass].class_name; ++ tclass_ex = string_to_security_class(tclass_name); ++ ++ if (tclass_ex == 0) ++ { ++ /* ++ * If the current security policy does not support permissions ++ * corresponding to database objects, we fill up them with dummy ++ * data. ++ * If security_deny_unknown() returns positive value, undefined ++ * permissions should be denied. Otherwise, allowed ++ */ ++ avd->allowed = (deny_unknown > 0 ? 0 : ~0U); ++ avd->auditallow = 0U; ++ avd->auditdeny = ~0U; ++ avd->flags = 0; ++ ++ return; ++ } ++ ++ /* ++ * Ask SELinux what is allowed set of permissions on a pair of the ++ * security contexts and the given object class. ++ */ ++ if (security_compute_av_flags_raw(scontext, tcontext, ++ tclass_ex, 0, &avd_ex) < 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux could not compute av_decision: " ++ "scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, tclass_name))); ++ ++ /* ++ * SELinux returns its access control decision as a set of permissions ++ * represented in external code which depends on run-time environment. ++ * So, we need to translate it to the internal representation before ++ * returning results for the caller. ++ */ ++ memset(avd, 0, sizeof(struct av_decision)); ++ ++ for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++) ++ { ++ access_vector_t perm_code_ex; ++ const char *perm_name = selinux_catalog[tclass].perms[i].perm_name; ++ uint32 perm_code = selinux_catalog[tclass].perms[i].perm_code; ++ ++ perm_code_ex = string_to_av_perm(tclass_ex, perm_name); ++ if (perm_code_ex == 0) ++ { ++ /* fill up undefined permissions */ ++ if (!deny_unknown) ++ avd->allowed |= perm_code; ++ avd->auditdeny |= perm_code; ++ ++ continue; ++ } ++ ++ if (avd_ex.allowed & perm_code_ex) ++ avd->allowed |= perm_code; ++ if (avd_ex.auditallow & perm_code_ex) ++ avd->auditallow |= perm_code; ++ if (avd_ex.auditdeny & perm_code_ex) ++ avd->auditdeny |= perm_code; ++ } ++ ++ return; ++} ++ ++/* ++ * sepgsql_compute_perms ++ * ++ * It makes access control decision communicating with SELinux. ++ * If SELinux does not allow required permissions on a pair of the security ++ * contexts, it raises an error or returns false. ++ * ++ * scontext : The security context of subject. In most cases, it is client. ++ * tcontext : The security context of target database object. ++ * tclass : One of the object class code (SEPG_CLASS_*) declared in the ++ * header file. ++ * required : A bitmap of the required permissions (SEPG___) ++ * declared in the header file. ++ * audit_name : A human readable name of the database object for auditing. ++ * abort : True, if caller want to raise an error on access violation. ++ */ ++bool ++sepgsql_compute_perms(char *scontext, char *tcontext, ++ uint16 tclass, uint32 required, ++ const char *audit_name, bool abort) ++{ ++ struct av_decision avd; ++ uint32 denied; ++ uint32 audited; ++ ++ sepgsql_compute_avd(scontext, tcontext, tclass, &avd); ++ ++ /* ++ * It logs a security audit record for the given request, if necessary. ++ * When SE-PgSQL performs 'internal' mode, it needs to keep silent. ++ */ ++ denied = required & ~avd.allowed; ++ if (sepgsql_debug_audit && tclass != SEPG_CLASS_DB_TUPLE) ++ audited = (denied ? (denied & ~0) : (required & ~0)); ++ else ++ audited = (denied ? (denied & avd.auditdeny) ++ : (required & avd.auditallow)); ++ ++ if (audited && sepostgresql_mode != SEPGSQL_MODE_INTERNAL) ++ { ++ sepgsql_audit_log(!!denied, scontext, tcontext, ++ tclass, audited, audit_name); ++ } ++ ++ /* ++ * If here is no policy violations, or SE-PgSQL performs in permissive ++ * mode, or the client process peforms in permissive domain, it returns ++ * normally with 'true'. ++ */ ++ if (!denied || ++ !sepgsql_get_enforce() || ++ (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) != 0) ++ return true; ++ ++ /* ++ * Otherwise, it raises an error or returns 'false', depending on the ++ * caller's indication by 'abort'. ++ */ ++ if (abort) ++ ereport(ERROR, ++ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ++ errmsg("SELinux: security policy violation"))); ++ ++ return false; ++} ++ ++/* ++ * sepgsql_compute_create ++ * ++ * It returns a default security context to be assigned on a new database ++ * object. SELinux compute it based on a combination of client, upper object ++ * which owns the new object and object class. ++ * ++ * For example, when a client (staff_u:staff_r:staff_t:s0) tries to create ++ * a new table within a schema (system_u:object_r:sepgsql_schema_t:s0), ++ * SELinux looks-up its security policy. If it has a special rule on the ++ * combination of these security contexts and object class (db_table), ++ * it returns the security context suggested by the special rule. ++ * Otherwise, it returns the security context of schema, as is. ++ * ++ * We expect the caller already applies sanity/validation checks on the ++ * given security context. ++ * ++ * scontext : The security context of subject. In most cases, it is client. ++ * tcontext : The security context of the parent database object.. ++ * tclass : One of the object class code (SEPG_CLASS_*) declared in the ++ * header file. ++ */ ++char * ++sepgsql_compute_create(char *scontext, char *tcontext, uint16 tclass) ++{ ++ security_context_t ncontext; ++ security_class_t tclass_ex; ++ const char *tclass_name; ++ char *result; ++ ++ /* Get external code of the object class*/ ++ Assert(tclass < SEPG_CLASS_MAX); ++ ++ tclass_name = selinux_catalog[tclass].class_name; ++ tclass_ex = string_to_security_class(tclass_name); ++ ++ /* ++ * Ask SELinux what is the default context for the given object class ++ * on a pair of security contexts ++ */ ++ if (security_compute_create_raw(scontext, tcontext, ++ tclass_ex, &ncontext)) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux could not compute a new context: " ++ "scontext=%s tcontext=%s tclass=%s", ++ scontext, tcontext, tclass_name))); ++ ++ /* ++ * libselinux returns malloc()'ed string, so we need to copy it ++ * on the palloc()'ed region. ++ */ ++ PG_TRY(); ++ { ++ result = pstrdup(ncontext); ++ } ++ PG_CATCH(); ++ { ++ freecon(ncontext); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(ncontext); ++ ++ return result; ++} +diff --git a/src/backend/sepgsql/tablespace.c b/src/backend/sepgsql/tablespace.c +new file mode 100644 +index 0000000..1d3a35e +--- /dev/null ++++ b/src/backend/sepgsql/tablespace.c +@@ -0,0 +1,157 @@ ++/* ++ * tablespace.c ++ * ++ * SELinux hooks related to tablespaces ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_tablespace.h" ++#include "catalog/pg_seclabel.h" ++#include "commands/tablespace.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/syscache.h" ++#include "utils/lsyscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_tablespace_common(Oid tablespaceOid, uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ bool retval; ++ const char *auname; ++ ++ tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(tablespaceOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for tablespace %u", tablespaceOid); ++ ++ tsid.relid = TableSpaceRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname); ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++#endif ++ ++Oid ++sepgsql_tablespace_create(const char *tablespaceName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ nsid = sepgsql_get_default_tuple_secid(TableSpaceRelationId); ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ tablespaceName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_tablespace_alter(Oid tablespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_tablespace_common(tablespaceOid, ++ SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_tablespace_relabel(Oid tablespaceOid, char *newLabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ ++ tsid.relid = TableSpaceRelationId; ++ tsid.secid = seclabelTransInput(tsid.relid, newLabel); ++ ++ /* db_tuple:{update relabelfrom} */ ++ sepgsql_tablespace_common(tablespaceOid, ++ SEPG_DB_TUPLE__UPDATE | ++ SEPG_DB_TUPLE__RELABELFROM, true); ++ ++ /* db_procedure:{relabelto} */ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELTO, ++ get_tablespace_name(tablespaceOid), ++ true); ++ return tsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_tablespace_drop(Oid tablespaceOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_tablespace_common(tablespaceOid, ++ SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_tablespace_grant(Oid tablespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_tablespace_common(tablespaceOid, ++ SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_tablespace_getattr(Oid tablespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_tablespace_common(tablespaceOid, ++ SEPG_DB_TUPLE__SELECT, true); ++ } ++#endif ++} ++ ++void ++sepgsql_tablespace_comment(Oid tablespaceOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_tablespace_common(tablespaceOid, ++ SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/tsearch.c b/src/backend/sepgsql/tsearch.c +new file mode 100644 +index 0000000..3d4180d +--- /dev/null ++++ b/src/backend/sepgsql/tsearch.c +@@ -0,0 +1,524 @@ ++/* ++ * tsearch.c ++ * ++ * SELinux hooks related to text searches ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_ts_config.h" ++#include "catalog/pg_ts_dict.h" ++#include "catalog/pg_ts_parser.h" ++#include "catalog/pg_ts_template.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_ts_config_common(Oid confOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(confOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search configuration %u", confOid); ++ ++ tsid.relid = TSConfigRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_ts_config) GETSTRUCT(tuple))->cfgname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_ts_config_namespace(Oid confOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(confOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++bool ++sepgsql_ts_dict_common(Oid dictOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); ++ ++ tsid.relid = TSDictionaryRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_ts_dict_namespace(Oid dictOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++bool ++sepgsql_ts_parser_common(Oid parseOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(parseOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search parser %u", parseOid); ++ ++ tsid.relid = TSParserRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_ts_parser) GETSTRUCT(tuple))->prsname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_ts_parser_namespace(Oid parseOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(parseOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++bool ++sepgsql_ts_template_common(Oid templateOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ HeapTuple tuple; ++ const char *auname; ++ bool retval; ++ ++ tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(templateOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for text search template %u", templateOid); ++ ++ tsid.relid = TSDictionaryRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ auname = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_ts_template_namespace(Oid templateOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId = InvalidOid; ++ ++ tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(templateOid)); ++ if (HeapTupleIsValid(tuple)) ++ { ++ namespaceId = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace; ++ ++ ReleaseSysCache(tuple); ++ } ++ return namespaceId; ++} ++ ++#endif ++ ++Oid ++sepgsql_ts_config_create(const char *confName, Oid namespaceId) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(TSConfigRelationId); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ confName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_ts_config_alter(Oid confOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_config_alter_rename(Oid confOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_config_namespace(confOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_config_drop(Oid confOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_config_namespace(confOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_config_comment(Oid confOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_ts_dict_create(const char *dictName, Oid namespaceId) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(TSDictionaryRelationId); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ dictName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_ts_dict_alter(Oid dictOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_dict_alter_rename(Oid dictOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_dict_namespace(dictOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_dict_drop(Oid dictOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_dict_namespace(dictOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_dict_comment(Oid dictOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_ts_parser_create(const char *parseName, Oid namespaceId, ++ Oid startFunc, Oid tokenFunc, Oid endFunc, ++ Oid headlineFunc, Oid lextypeFunc) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(TSParserRelationId); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(startFunc)) ++ sepgsql_proc_common(startFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(tokenFunc)) ++ sepgsql_proc_common(tokenFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(endFunc)) ++ sepgsql_proc_common(endFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(headlineFunc)) ++ sepgsql_proc_common(headlineFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(lextypeFunc)) ++ sepgsql_proc_common(lextypeFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ parseName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_ts_parser_alter_rename(Oid parseOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_parser_namespace(parseOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_parser_drop(Oid parseOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_parser_namespace(parseOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_parser_comment(Oid parseOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_ts_template_create(const char *templateName, Oid namespaceId, ++ Oid initFunc, Oid lexizeFunc) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid ++ = sepgsql_get_default_tuple_secid(TSTemplateRelationId); ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(initFunc)) ++ sepgsql_proc_common(initFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ if (OidIsValid(lexizeFunc)) ++ sepgsql_proc_common(lexizeFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_tuple:{insert} */ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ templateName, ++ true); ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_ts_template_alter_rename(Oid templateOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_template_namespace(templateOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_template_drop(Oid templateOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_ts_template_namespace(templateOid); ++ ++ /* db_schema:{remove_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{delete} */ ++ sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_ts_template_comment(Oid templateOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} +diff --git a/src/backend/sepgsql/type.c b/src/backend/sepgsql/type.c +new file mode 100644 +index 0000000..8cd65ae +--- /dev/null ++++ b/src/backend/sepgsql/type.c +@@ -0,0 +1,314 @@ ++/* ++ * type.c ++ * ++ * SELinux hooks related to types ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "catalog/pg_cast.h" ++#include "catalog/pg_seclabel.h" ++#include "catalog/pg_type.h" ++#include "sepgsql/hooks.h" ++#include "sepgsql/sepgsql.h" ++#include "utils/builtins.h" ++#include "utils/lsyscache.h" ++#include "utils/syscache.h" ++ ++#ifdef HAVE_SELINUX ++bool ++sepgsql_type_common(Oid typeOid, uint32 required, bool abort) ++{ ++ sepgsql_sid_t tsid; ++ char *auname; ++ bool retval; ++ ++ tsid.relid = TypeRelationId; ++ tsid.secid = GetSysCacheSecid1(TYPEOID, ++ ObjectIdGetDatum(typeOid)); ++ ++ auname = format_type_be(typeOid); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ auname, ++ abort); ++ pfree(auname); ++ ++ return retval; ++} ++ ++bool ++sepgsql_cast_common(Oid srcTypeOid, Oid dstTypeOid, ++ uint32 required, bool abort) ++{ ++ HeapTuple tuple; ++ sepgsql_sid_t tsid; ++ bool retval; ++ ++ tuple = SearchSysCache2(CASTSOURCETARGET, ++ ObjectIdGetDatum(srcTypeOid), ++ ObjectIdGetDatum(dstTypeOid)); ++ if (!HeapTupleIsValid(tuple)) ++ elog(ERROR, "cache lookup failed for cast (%u,%u)", ++ srcTypeOid, dstTypeOid); ++ ++ tsid.relid = CastRelationId; ++ tsid.secid = HeapTupleGetSecid(tuple); ++ ++ retval = sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ NULL, ++ abort); ++ ReleaseSysCache(tuple); ++ ++ return retval; ++} ++ ++static Oid ++get_type_namespace(Oid typeOid) ++{ ++ HeapTuple tuple; ++ Oid namespaceId; ++ ++ tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid)); ++ if (!HeapTupleIsValid(tuple)) ++ return InvalidOid; ++ ++ namespaceId = ((Form_pg_type) GETSTRUCT(tuple))->typnamespace; ++ ++ ReleaseSysCache(tuple); ++ ++ return namespaceId; ++} ++#endif ++ ++Oid ++sepgsql_type_create(const char *typeName, Oid replaced, ++ Oid namespaceId, char typeType, ++ Oid inputFunc, Oid outputFunc, ++ Oid recvFunc, Oid sendFunc, ++ Oid modinFunc, Oid modoutFunc, Oid analyzeFunc) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ uint32 required; ++ ++ /* db_schema:{add_name} */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_tuple:{insert or update} */ ++ if (!OidIsValid(replaced)) ++ { ++ nsid = sepgsql_get_default_tuple_secid(TypeRelationId); ++ required = SEPG_DB_TUPLE__INSERT; ++ } ++ else ++ { ++ nsid.relid = TypeRelationId; ++ nsid.secid = GetSysCacheSecid1(TYPEOID, ++ ObjectIdGetDatum(replaced)); ++ required = SEPG_DB_TUPLE__UPDATE; ++ } ++ ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ required, ++ typeName, ++ true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(inputFunc)) ++ sepgsql_proc_common(inputFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(outputFunc)) ++ sepgsql_proc_common(outputFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(recvFunc)) ++ sepgsql_proc_common(recvFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(sendFunc)) ++ sepgsql_proc_common(sendFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(modinFunc)) ++ sepgsql_proc_common(modinFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(modoutFunc)) ++ sepgsql_proc_common(modoutFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ /* db_procedure:{install} */ ++ if (OidIsValid(analyzeFunc)) ++ sepgsql_proc_common(analyzeFunc, SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_type_alter(Oid typeOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_type_alter_rename(Oid typeOid, const char *newName) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_type_namespace(typeOid); ++ ++ /* db_schema:{add_name remove_name} */ ++ sepgsql_schema_common(namespaceId, ++ SEPG_DB_SCHEMA__ADD_NAME | ++ SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_type_alter_schema(Oid typeOid, Oid newSchema) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ Oid namespaceId = get_type_namespace(typeOid); ++ ++ /* db_schema:{ remove_name } */ ++ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); ++ ++ /* db_schema:{ add_name } */ ++ sepgsql_schema_common(newSchema, SEPG_DB_SCHEMA__ADD_NAME, true); ++ ++ /* db_tuple:{update} */ ++ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_type_relabel(Oid typeOid, char *newLabel) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t tsid; ++ ++ tsid.relid = TypeRelationId; ++ tsid.secid = seclabelTransInput(tsid.relid, newLabel); ++ ++ /* db_tuple:{update relabelfrom} */ ++ sepgsql_type_common(typeOid, ++ SEPG_DB_TUPLE__UPDATE | ++ SEPG_DB_TUPLE__RELABELFROM, true); ++ ++ /* db_procedure:{relabelto} */ ++ sepgsql_client_perms(tsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__RELABELTO, ++ format_type_be(typeOid), ++ true); ++ return tsid.secid; ++ } ++#endif ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("SE-PostgreSQL is not available"))); ++ return InvalidOid; ++} ++ ++void ++sepgsql_type_drop(Oid typeOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{delete} */ ++ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_type_comment(Oid typeOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ /* db_tuple:{update} */ ++ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} ++ ++Oid ++sepgsql_cast_create(Oid sourceTypeOid, Oid targetTypeOid, ++ char castMethod, Oid castFuncOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_sid_t nsid; ++ ++ /* db_tuple:{insert} */ ++ nsid = sepgsql_get_default_tuple_secid(CastRelationId); ++ sepgsql_client_perms(nsid, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_DB_TUPLE__INSERT, ++ NULL, ++ true); ++ /* db_procedure:{install} */ ++ if (OidIsValid(castFuncOid)) ++ sepgsql_proc_common(castFuncOid, ++ SEPG_DB_PROCEDURE__INSTALL, true); ++ ++ return nsid.secid; ++ } ++#endif ++ return InvalidOid; ++} ++ ++void ++sepgsql_cast_drop(Oid sourceTypeOid, Oid targetTypeOid, bool cascade) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_cast_common(sourceTypeOid, targetTypeOid, ++ SEPG_DB_TUPLE__DELETE, true); ++ } ++#endif ++} ++ ++void ++sepgsql_cast_comment(Oid sourceTypeOid, Oid targetTypeOid) ++{ ++#ifdef HAVE_SELINUX ++ if (sepgsql_is_enabled()) ++ { ++ sepgsql_cast_common(sourceTypeOid, targetTypeOid, ++ SEPG_DB_TUPLE__UPDATE, true); ++ } ++#endif ++} +diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c +index ca48cdd..bb0688f 100644 +--- a/src/backend/storage/large_object/inv_api.c ++++ b/src/backend/storage/large_object/inv_api.c +@@ -197,14 +197,14 @@ getbytealen(bytea *data) + * in use. + */ + Oid +-inv_create(Oid lobjId) ++inv_create(Oid lobjId, Oid securityId) + { + Oid lobjId_new; + + /* + * Create a new largeobject with empty data pages + */ +- lobjId_new = LargeObjectCreate(lobjId); ++ lobjId_new = LargeObjectCreate(lobjId, securityId); + + /* + * dependency on the owner of largeobject +diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c +index 575fa86..eb2b5e8 100644 +--- a/src/backend/tcop/fastpath.c ++++ b/src/backend/tcop/fastpath.c +@@ -26,6 +26,7 @@ + #include "libpq/pqformat.h" + #include "mb/pg_wchar.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "tcop/fastpath.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" +@@ -347,6 +348,10 @@ HandleFunctionRequest(StringInfo msgBuf) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(fid)); + ++ /* SELinux checks */ ++ sepgsql_schema_search(fip->namespace, true); ++ sepgsql_proc_execute(fid); ++ + /* + * Prepare function call info block and insert arguments. + */ +diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c +index 8ad4915..54961d8 100644 +--- a/src/backend/tcop/pquery.c ++++ b/src/backend/tcop/pquery.c +@@ -575,7 +575,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) + Assert(pstmt->hasReturning); + portal->tupDesc = + ExecCleanTypeFromTL(pstmt->planTree->targetlist, +- false); ++ false, false); + } + + /* +diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c +index 8960246..e8f8ea3 100644 +--- a/src/backend/tcop/utility.c ++++ b/src/backend/tcop/utility.c +@@ -165,6 +165,7 @@ check_xact_readonly(Node *parsetree) + case T_AlterRoleSetStmt: + case T_AlterObjectSchemaStmt: + case T_AlterOwnerStmt: ++ case T_AlterSecLabelStmt: + case T_AlterSeqStmt: + case T_AlterTableStmt: + case T_RenameStmt: +@@ -696,6 +697,10 @@ standard_ProcessUtility(Node *parsetree, + ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); + break; + ++ case T_AlterSecLabelStmt: ++ ExecAlterSecLabelStmt((AlterSecLabelStmt *) parsetree); ++ break; ++ + case T_AlterTableStmt: + { + List *stmts; +@@ -1760,6 +1765,46 @@ CreateCommandTag(Node *parsetree) + } + break; + ++ case T_AlterSecLabelStmt: ++ switch (((AlterSecLabelStmt *) parsetree)->objectType) ++ { ++ case OBJECT_DATABASE: ++ tag = "ALTER DATABASE"; ++ break; ++ case OBJECT_SCHEMA: ++ tag = "ALTER SCHEMA"; ++ break; ++ case OBJECT_TABLE: ++ case OBJECT_COLUMN: ++ tag = "ALTER TABLE"; ++ break; ++ case OBJECT_SEQUENCE: ++ tag = "ALTER SEQUENCE"; ++ break; ++ case OBJECT_VIEW: ++ tag = "ALTER VIEW"; ++ break; ++ case OBJECT_FUNCTION: ++ tag = "ALTER FUNCTION"; ++ break; ++ case OBJECT_AGGREGATE: ++ tag = "ALTER AGGREGATE"; ++ break; ++ case OBJECT_LARGEOBJECT: ++ tag = "ALTER LARGE OBJECT"; ++ break; ++ case OBJECT_TYPE: ++ tag = "ALTER TYPE"; ++ break; ++ case OBJECT_DOMAIN: ++ tag = "ALTER DOMAIN"; ++ break; ++ default: ++ tag = "???"; ++ break; ++ } ++ break; ++ + case T_AlterTableStmt: + switch (((AlterTableStmt *) parsetree)->relkind) + { +@@ -2352,6 +2397,10 @@ GetCommandLogLevel(Node *parsetree) + lev = LOGSTMT_DDL; + break; + ++ case T_AlterSecLabelStmt: ++ lev = LOGSTMT_DDL; ++ break; ++ + case T_AlterTableStmt: + lev = LOGSTMT_DDL; + break; +diff --git a/src/backend/tsearch/wparser.c b/src/backend/tsearch/wparser.c +index 0fed35c..ca9e9d1 100644 +--- a/src/backend/tsearch/wparser.c ++++ b/src/backend/tsearch/wparser.c +@@ -59,7 +59,7 @@ tt_setup_firstcall(FuncCallContext *funcctx, Oid prsid) + (Datum) 0)); + funcctx->user_fctx = (void *) st; + +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "alias", +@@ -205,7 +205,7 @@ prs_setup_firstcall(FuncCallContext *funcctx, Oid prsid, text *txt) + st->cur = 0; + + funcctx->user_fctx = (void *) st; +- tupdesc = CreateTemplateTupleDesc(2, false); ++ tupdesc = CreateTemplateTupleDesc(2, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "token", +diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c +index faad11e..53e6944 100644 +--- a/src/backend/utils/adt/acl.c ++++ b/src/backend/utils/adt/acl.c +@@ -1695,7 +1695,7 @@ aclexplode(PG_FUNCTION_ARGS) + * build tupdesc for result tuples (matches out parameters in pg_proc + * entry) + */ +- tupdesc = CreateTemplateTupleDesc(4, false); ++ tupdesc = CreateTemplateTupleDesc(4, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "grantor", + OIDOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee", +diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c +index ed81ced..f3d4b99 100644 +--- a/src/backend/utils/adt/datetime.c ++++ b/src/backend/utils/adt/datetime.c +@@ -4193,7 +4193,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS) + * build tupdesc for result tuples. This must match this function's + * pg_proc entry! + */ +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "abbrev", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "utc_offset", +@@ -4286,7 +4286,7 @@ pg_timezone_names(PG_FUNCTION_ARGS) + * build tupdesc for result tuples. This must match this function's + * pg_proc entry! + */ +- tupdesc = CreateTemplateTupleDesc(4, false); ++ tupdesc = CreateTemplateTupleDesc(4, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "abbrev", +diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c +index a4e0252..36d07b7 100644 +--- a/src/backend/utils/adt/dbsize.c ++++ b/src/backend/utils/adt/dbsize.c +@@ -21,6 +21,7 @@ + #include "commands/dbcommands.h" + #include "commands/tablespace.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -90,6 +91,9 @@ calculate_database_size(Oid dbOid) + aclcheck_error(aclresult, ACL_KIND_DATABASE, + get_database_name(dbOid)); + ++ /* SELinux checks */ ++ sepgsql_database_getattr(dbOid); ++ + /* Shared storage in pg_global is not counted */ + + /* Include pg_default storage */ +@@ -178,6 +182,8 @@ calculate_tablespace_size(Oid tblspcOid) + aclcheck_error(aclresult, ACL_KIND_TABLESPACE, + get_tablespace_name(tblspcOid)); + } ++ /* SELinux checks */ ++ sepgsql_tablespace_getattr(tblspcOid); + + if (tblspcOid == DEFAULTTABLESPACE_OID) + snprintf(tblspcPath, MAXPGPATH, "base"); +diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c +index e074b79..8bb707f 100644 +--- a/src/backend/utils/adt/genfile.c ++++ b/src/backend/utils/adt/genfile.c +@@ -25,6 +25,7 @@ + #include "mb/pg_wchar.h" + #include "miscadmin.h" + #include "postmaster/syslogger.h" ++#include "sepgsql/hooks.h" + #include "storage/fd.h" + #include "utils/builtins.h" + #include "utils/memutils.h" +@@ -100,6 +101,8 @@ pg_read_file(PG_FUNCTION_ARGS) + + filename = convert_and_check_filename(filename_t); + ++ sepgsql_file_read(filename); ++ + if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL) + ereport(ERROR, + (errcode_for_file_access(), +@@ -164,6 +167,8 @@ pg_stat_file(PG_FUNCTION_ARGS) + + filename = convert_and_check_filename(filename_t); + ++ sepgsql_file_getattr(filename); ++ + if (stat(filename, &fst) < 0) + ereport(ERROR, + (errcode_for_file_access(), +@@ -173,7 +178,7 @@ pg_stat_file(PG_FUNCTION_ARGS) + * This record type had better match the output parameters declared for me + * in pg_proc.h. + */ +- tupdesc = CreateTemplateTupleDesc(6, false); ++ tupdesc = CreateTemplateTupleDesc(6, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, + "size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, +@@ -236,6 +241,8 @@ pg_ls_dir(PG_FUNCTION_ARGS) + fctx = palloc(sizeof(directory_fctx)); + fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0)); + ++ sepgsql_dir_list(fctx->location); ++ + fctx->dirdesc = AllocateDir(fctx->location); + + if (!fctx->dirdesc) +diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c +index 07e6aab..e5c8182 100644 +--- a/src/backend/utils/adt/lockfuncs.c ++++ b/src/backend/utils/adt/lockfuncs.c +@@ -85,7 +85,7 @@ pg_lock_status(PG_FUNCTION_ARGS) + + /* build tupdesc for result tuples */ + /* this had better match pg_locks view in system_views.sql */ +- tupdesc = CreateTemplateTupleDesc(14, false); ++ tupdesc = CreateTemplateTupleDesc(14, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database", +diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c +index 11342b2..0ddf907 100644 +--- a/src/backend/utils/adt/misc.c ++++ b/src/backend/utils/adt/misc.c +@@ -322,7 +322,7 @@ pg_get_keywords(PG_FUNCTION_ARGS) + funcctx = SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catcode", +diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c +index 8379407..d44655b 100644 +--- a/src/backend/utils/adt/pgstatfuncs.c ++++ b/src/backend/utils/adt/pgstatfuncs.c +@@ -419,7 +419,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + +- tupdesc = CreateTemplateTupleDesc(11, false); ++ tupdesc = CreateTemplateTupleDesc(11, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0); +diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c +index 9db070d..6881976 100644 +--- a/src/backend/utils/adt/ri_triggers.c ++++ b/src/backend/utils/adt/ri_triggers.c +@@ -30,6 +30,7 @@ + + #include "postgres.h" + ++#include "access/sysattr.h" + #include "access/xact.h" + #include "catalog/pg_constraint.h" + #include "catalog/pg_operator.h" +@@ -39,6 +40,7 @@ + #include "parser/parse_coerce.h" + #include "parser/parse_relation.h" + #include "miscadmin.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -2624,6 +2626,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) + char fkrelname[MAX_QUOTED_REL_NAME_LEN]; + char pkattname[MAX_QUOTED_NAME_LEN + 3]; + char fkattname[MAX_QUOTED_NAME_LEN + 3]; ++ Bitmapset *pkColumns = NULL; ++ Bitmapset *fkColumns = NULL; + const char *sep; + int i; + int old_work_mem; +@@ -2645,6 +2649,18 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) + + ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false); + ++ for (i = 0; i < riinfo.nkeys; i++) ++ { ++ fkColumns = bms_add_member(fkColumns, riinfo.fk_attnums[i] ++ - FirstLowInvalidHeapAttributeNumber); ++ pkColumns = bms_add_member(pkColumns, riinfo.pk_attnums[i] ++ - FirstLowInvalidHeapAttributeNumber); ++ } ++ ++ if (!sepgsql_relation_perms(RelationGetRelid(pk_rel), ++ ACL_SELECT, pkColumns, NULL, false)) ++ return false; ++ + /*---------- + * The query string built is: + * SELECT fk.keycols FROM ONLY relname fk +@@ -3259,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]; + +@@ -3342,6 +3359,10 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan, + SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner, + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); + ++ /* Switch rowlv control mode */ ++ temp_rowlv = (detectNewRows ? SEPGSQL_ROWLV_ABORT : SEPGSQL_ROWLV_FILTER); ++ save_rowlv = sepgsql_rowlv_set_mode(temp_rowlv); ++ + /* Finally we can run the query. */ + spi_result = SPI_execute_snapshot(qplan, + vals, nulls, +@@ -3351,6 +3372,9 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan, + /* Restore UID and security context */ + SetUserIdAndSecContext(save_userid, save_sec_context); + ++ /* Restore Rowlv control mode */ ++ sepgsql_rowlv_set_mode(save_rowlv); ++ + /* Check result */ + if (spi_result < 0) + elog(ERROR, "SPI_execute_snapshot returned %d", spi_result); +diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c +index c837e67..1bf4eb8 100644 +--- a/src/backend/utils/adt/tid.c ++++ b/src/backend/utils/adt/tid.c +@@ -27,6 +27,7 @@ + #include "libpq/pqformat.h" + #include "miscadmin.h" + #include "parser/parsetree.h" ++#include "sepgsql/hooks.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/rel.h" +@@ -347,6 +348,8 @@ currtid_byreloid(PG_FUNCTION_ARGS) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_relation_getattr(RelationGetRelid(rel)); + + if (rel->rd_rel->relkind == RELKIND_VIEW) + return currtid_for_view(rel, tid); +@@ -377,6 +380,8 @@ currtid_byrelname(PG_FUNCTION_ARGS) + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_relation_getattr(RelationGetRelid(rel)); + + if (rel->rd_rel->relkind == RELKIND_VIEW) + return currtid_for_view(rel, tid); +diff --git a/src/backend/utils/adt/trigfuncs.c b/src/backend/utils/adt/trigfuncs.c +index 70246fb..b996537 100644 +--- a/src/backend/utils/adt/trigfuncs.c ++++ b/src/backend/utils/adt/trigfuncs.c +@@ -76,6 +76,10 @@ suppress_redundant_updates_trigger(PG_FUNCTION_ARGS) + !OidIsValid(HeapTupleHeaderGetOid(newheader))) + HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader)); + ++ if (trigdata->tg_relation->rd_rel->relhassecids && ++ !OidIsValid(HeapTupleHeaderGetSecid(newheader))) ++ HeapTupleHeaderSetSecid(newheader, HeapTupleHeaderGetSecid(oldheader)); ++ + /* if the tuple payload is the same ... */ + if (newtuple->t_len == oldtuple->t_len && + newheader->t_hoff == oldheader->t_hoff && +diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c +index 78f08f4..4a44810 100644 +--- a/src/backend/utils/adt/tsvector_op.c ++++ b/src/backend/utils/adt/tsvector_op.c +@@ -975,7 +975,7 @@ ts_setup_firstcall(FunctionCallInfo fcinfo, FuncCallContext *funcctx, + } + Assert(stat->stackpos <= stat->maxdepth); + +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "ndoc", +diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c +index 88d8d8b..281acb0 100644 +--- a/src/backend/utils/cache/plancache.c ++++ b/src/backend/utils/cache/plancache.c +@@ -924,12 +924,12 @@ PlanCacheComputeResultDesc(List *stmt_list) + if (IsA(node, Query)) + { + query = (Query *) node; +- return ExecCleanTypeFromTL(query->targetList, false); ++ return ExecCleanTypeFromTL(query->targetList, false, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; +- return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); ++ return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false); + } + /* other cases shouldn't happen, but return NULL */ + break; +@@ -940,13 +940,13 @@ PlanCacheComputeResultDesc(List *stmt_list) + { + query = (Query *) node; + Assert(query->returningList); +- return ExecCleanTypeFromTL(query->returningList, false); ++ return ExecCleanTypeFromTL(query->returningList, false, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; + Assert(pstmt->hasReturning); +- return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); ++ return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false); + } + /* other cases shouldn't happen, but return NULL */ + break; +diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c +index d462510..c704bc9 100644 +--- a/src/backend/utils/cache/relcache.c ++++ b/src/backend/utils/cache/relcache.c +@@ -216,7 +216,7 @@ static void write_relcache_init_file(bool shared); + static void write_item(const void *data, Size len, FILE *fp); + + static void formrdesc(const char *relationName, Oid relationReltype, +- bool isshared, bool hasoids, ++ bool isshared, bool hasoids, bool hassecids, + int natts, const FormData_pg_attribute *attrs); + + static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK); +@@ -355,7 +355,8 @@ AllocateRelationDesc(Form_pg_class relp) + + /* and allocate attribute tuple form storage */ + relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, +- relationForm->relhasoids); ++ relationForm->relhasoids, ++ relationForm->relhassecids); + /* which we mark as a reference-counted tupdesc */ + relation->rd_att->tdrefcount = 1; + +@@ -437,6 +438,7 @@ RelationBuildTupleDesc(Relation relation) + relation->rd_att->tdtypeid = relation->rd_rel->reltype; + relation->rd_att->tdtypmod = -1; /* unnecessary, but... */ + relation->rd_att->tdhasoid = relation->rd_rel->relhasoids; ++ relation->rd_att->tdhassecid = relation->rd_rel->relhassecids; + + constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext, + sizeof(TupleConstr)); +@@ -1396,7 +1398,7 @@ LookupOpclassInfo(Oid operatorClassOid, + */ + static void + formrdesc(const char *relationName, Oid relationReltype, +- bool isshared, bool hasoids, ++ bool isshared, bool hasoids, bool hassecids, + int natts, const FormData_pg_attribute *attrs) + { + Relation relation; +@@ -1459,6 +1461,7 @@ formrdesc(const char *relationName, Oid relationReltype, + relation->rd_rel->reltuples = 1; + relation->rd_rel->relkind = RELKIND_RELATION; + relation->rd_rel->relhasoids = hasoids; ++ relation->rd_rel->relhassecids = hassecids; + relation->rd_rel->relnatts = (int16) natts; + + /* +@@ -1468,7 +1471,7 @@ formrdesc(const char *relationName, Oid relationReltype, + * because it will never be replaced. The data comes from + * src/include/catalog/ headers via genbki.pl. + */ +- relation->rd_att = CreateTemplateTupleDesc(natts, hasoids); ++ relation->rd_att = CreateTemplateTupleDesc(natts, hasoids, hassecids); + relation->rd_att->tdrefcount = 1; /* mark as refcounted */ + + relation->rd_att->tdtypeid = relationReltype; +@@ -2551,6 +2554,7 @@ RelationBuildLocalRelation(const char *relname, + + rel->rd_rel->relkind = RELKIND_UNCATALOGED; + rel->rd_rel->relhasoids = rel->rd_att->tdhasoid; ++ rel->rd_rel->relhassecids = rel->rd_att->tdhassecid; + rel->rd_rel->relnatts = natts; + rel->rd_rel->reltype = InvalidOid; + /* needed when bootstrapping: */ +@@ -2793,11 +2797,11 @@ RelationCacheInitializePhase2(void) + if (!load_relcache_init_file(true)) + { + formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true, +- true, Natts_pg_database, Desc_pg_database); ++ true, true, Natts_pg_database, Desc_pg_database); + formrdesc("pg_authid", AuthIdRelation_Rowtype_Id, true, +- true, Natts_pg_authid, Desc_pg_authid); ++ true, true, Natts_pg_authid, Desc_pg_authid); + formrdesc("pg_auth_members", AuthMemRelation_Rowtype_Id, true, +- false, Natts_pg_auth_members, Desc_pg_auth_members); ++ false, false, Natts_pg_auth_members, Desc_pg_auth_members); + + #define NUM_CRITICAL_SHARED_RELS 3 /* fix if you change list above */ + } +@@ -2848,13 +2852,13 @@ RelationCacheInitializePhase3(void) + needNewCacheFile = true; + + formrdesc("pg_class", RelationRelation_Rowtype_Id, false, +- true, Natts_pg_class, Desc_pg_class); ++ true, true, Natts_pg_class, Desc_pg_class); + formrdesc("pg_attribute", AttributeRelation_Rowtype_Id, false, +- false, Natts_pg_attribute, Desc_pg_attribute); ++ false, true, Natts_pg_attribute, Desc_pg_attribute); + formrdesc("pg_proc", ProcedureRelation_Rowtype_Id, false, +- true, Natts_pg_proc, Desc_pg_proc); ++ true, true, Natts_pg_proc, Desc_pg_proc); + formrdesc("pg_type", TypeRelation_Rowtype_Id, false, +- true, Natts_pg_type, Desc_pg_type); ++ true, true, Natts_pg_type, Desc_pg_type); + + #define NUM_CRITICAL_LOCAL_RELS 4 /* fix if you change list above */ + } +@@ -3007,6 +3011,7 @@ RelationCacheInitializePhase3(void) + Assert(relation->rd_att->tdtypeid == relp->reltype); + Assert(relation->rd_att->tdtypmod == -1); + Assert(relation->rd_att->tdhasoid == relp->relhasoids); ++ Assert(relation->rd_att->tdhassecid == relp->relhassecids); + + ReleaseSysCache(htup); + +@@ -3117,7 +3122,7 @@ load_critical_index(Oid indexoid, Oid heapoid) + */ + static TupleDesc + BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, +- bool hasoids) ++ bool hasoids, bool hassecids) + { + TupleDesc result; + MemoryContext oldcxt; +@@ -3125,7 +3130,7 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, + + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + +- result = CreateTemplateTupleDesc(natts, hasoids); ++ result = CreateTemplateTupleDesc(natts, hasoids, hassecids); + result->tdtypeid = RECORDOID; /* not right, but we don't care */ + result->tdtypmod = -1; + +@@ -3155,7 +3160,7 @@ GetPgClassDescriptor(void) + if (pgclassdesc == NULL) + pgclassdesc = BuildHardcodedDescriptor(Natts_pg_class, + Desc_pg_class, +- true); ++ true, true); + + return pgclassdesc; + } +@@ -3169,7 +3174,7 @@ GetPgIndexDescriptor(void) + if (pgindexdesc == NULL) + pgindexdesc = BuildHardcodedDescriptor(Natts_pg_index, + Desc_pg_index, +- false); ++ false, false); + + return pgindexdesc; + } +@@ -3985,7 +3990,8 @@ load_relcache_init_file(bool shared) + + /* initialize attribute tuple forms */ + rel->rd_att = CreateTemplateTupleDesc(relform->relnatts, +- relform->relhasoids); ++ relform->relhasoids, ++ relform->relhassecids); + rel->rd_att->tdrefcount = 1; /* mark as refcounted */ + + rel->rd_att->tdtypeid = relform->reltype; +diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c +index 61b06ac..b08654b 100644 +--- a/src/backend/utils/cache/syscache.c ++++ b/src/backend/utils/cache/syscache.c +@@ -895,6 +895,30 @@ GetSysCacheOid(int cacheId, + return result; + } + ++/* ++ * GetSysCacheSecid ++ * ++ * A convenience routine that does SearchSysCache and returns the ++ * security-id of the found tuple, or InvalidOid if no tuple could ++ * be found. No lock is retained on the syscache entry. ++ */ ++Oid ++GetSysCacheSecid(int cacheId, ++ Datum key1, ++ Datum key2, ++ Datum key3, ++ Datum key4) ++{ ++ HeapTuple tuple; ++ Oid result; ++ ++ tuple = SearchSysCache(cacheId, key1, key2, key3, key4); ++ if (!HeapTupleIsValid(tuple)) ++ return InvalidOid; ++ result = HeapTupleGetSecid(tuple); ++ ReleaseSysCache(tuple); ++ return result; ++} + + /* + * SearchSysCacheAttName +diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c +index c3c0440..fb5b762 100644 +--- a/src/backend/utils/fmgr/fmgr.c ++++ b/src/backend/utils/fmgr/fmgr.c +@@ -24,6 +24,7 @@ + #include "miscadmin.h" + #include "nodes/nodeFuncs.h" + #include "pgstat.h" ++#include "sepgsql/hooks.h" + #include "utils/builtins.h" + #include "utils/fmgrtab.h" + #include "utils/guc.h" +@@ -190,6 +191,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, + finfo->fn_extra = NULL; + finfo->fn_mcxt = mcxt; + finfo->fn_expr = NULL; /* caller may set this later */ ++ finfo->fn_seclabel = NULL; + + if ((fbp = fmgr_isbuiltin(functionId)) != NULL) + { +@@ -228,15 +230,22 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, + * ability to set the track_functions GUC as a local GUC parameter of an + * interesting function and have the right things happen. + */ +- if (!ignore_security && +- (procedureStruct->prosecdef || +- !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig))) ++ if (!ignore_security) + { +- finfo->fn_addr = fmgr_security_definer; +- finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ +- finfo->fn_oid = functionId; +- ReleaseSysCache(procedureTuple); +- return; ++ char *seclabel ++ = sepgsql_proc_domtrans(procedureTuple, mcxt); ++ ++ if (procedureStruct->prosecdef || ++ !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig) || ++ seclabel != NULL) ++ { ++ finfo->fn_addr = fmgr_security_definer; ++ finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ ++ finfo->fn_oid = functionId; ++ finfo->fn_seclabel = seclabel; ++ ReleaseSysCache(procedureTuple); ++ return; ++ } + } + + switch (procedureStruct->prolang) +@@ -877,6 +886,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS) + FmgrInfo *save_flinfo; + Oid save_userid; + int save_sec_context; ++ char *save_seclabel; + volatile int save_nestlevel; + PgStat_FunctionCallUsage fcusage; + +@@ -939,6 +949,10 @@ fmgr_security_definer(PG_FUNCTION_ARGS) + PGC_S_SESSION, + GUC_ACTION_SAVE); + } ++ if (fcinfo->flinfo->fn_seclabel) ++ save_seclabel = sepgsql_set_client_label(fcinfo->flinfo->fn_seclabel); ++ else ++ save_seclabel = NULL; + + /* + * We don't need to restore GUC or userid settings on error, because the +@@ -978,6 +992,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS) + AtEOXact_GUC(true, save_nestlevel); + if (OidIsValid(fcache->userid)) + SetUserIdAndSecContext(save_userid, save_sec_context); ++ if (fcinfo->flinfo->fn_seclabel) ++ sepgsql_set_client_label(save_seclabel); + + return result; + } +diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c +index 382008c..e6eba47 100644 +--- a/src/backend/utils/fmgr/funcapi.c ++++ b/src/backend/utils/fmgr/funcapi.c +@@ -1105,7 +1105,7 @@ build_function_result_tupdesc_d(Datum proallargtypes, + if (numoutargs < 2) + return NULL; + +- desc = CreateTemplateTupleDesc(numoutargs, false); ++ desc = CreateTemplateTupleDesc(numoutargs, false, false); + for (i = 0; i < numoutargs; i++) + { + TupleDescInitEntry(desc, i + 1, +@@ -1220,7 +1220,7 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases) + /* OK, get the column alias */ + attname = strVal(linitial(colaliases)); + +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, + (AttrNumber) 1, + attname, +diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c +index 179d009..4bd7314 100644 +--- a/src/backend/utils/init/postinit.c ++++ b/src/backend/utils/init/postinit.c +@@ -37,6 +37,7 @@ + #include "postmaster/autovacuum.h" + #include "postmaster/postmaster.h" + #include "replication/walsender.h" ++#include "sepgsql/hooks.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "storage/ipc.h" +@@ -304,6 +305,9 @@ CheckMyDatabase(const char *name, bool am_superuser) + errmsg("permission denied for database \"%s\"", name), + errdetail("User does not have CONNECT privilege."))); + ++ /* SELinux checks */ ++ sepgsql_database_connect(MyDatabaseId); ++ + /* + * Check connection limit for this database. + * +@@ -800,6 +804,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, + /* set up ACL framework (so CheckMyDatabase can check permissions) */ + initialize_acl(); + ++ /* set up enhanced security feature */ ++ sepgsql_initialize(); ++ + /* + * Re-read the 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 920daeb..44fd3a9 100644 +--- a/src/backend/utils/misc/guc.c ++++ b/src/backend/utils/misc/guc.c +@@ -31,6 +31,7 @@ + #include "access/twophase.h" + #include "access/xact.h" + #include "catalog/namespace.h" ++#include "catalog/pg_seclabel.h" + #include "commands/async.h" + #include "commands/prepare.h" + #include "commands/vacuum.h" +@@ -56,6 +57,7 @@ + #include "postmaster/syslogger.h" + #include "postmaster/walwriter.h" + #include "replication/walsender.h" ++#include "sepgsql/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "tcop/tcopprot.h" +@@ -337,6 +339,18 @@ static const struct config_enum_entry constraint_exclusion_options[] = { + {NULL, 0, false} + }; + ++#ifdef HAVE_SELINUX ++static const struct config_enum_entry sepostgresql_options[] = { ++ {"default", SEPGSQL_MODE_DEFAULT, false}, ++ {"enforcing", SEPGSQL_MODE_ENFORCING, false}, ++ {"permissive", SEPGSQL_MODE_PERMISSIVE, false}, ++ {"disabled", SEPGSQL_MODE_DISABLED, false}, ++ {"on", SEPGSQL_MODE_DEFAULT, true}, ++ {"off", SEPGSQL_MODE_DISABLED, true}, ++ {NULL, 0, false}, ++}; ++#endif ++ + /* + * Options for enum values stored in other modules + */ +@@ -366,6 +380,7 @@ bool log_btree_build_stats = false; + + bool check_function_bodies = true; + bool default_with_oids = false; ++bool default_with_secids = true; + bool SQL_inheritance = true; + + bool Password_encryption = true; +@@ -1094,6 +1109,14 @@ static struct config_bool ConfigureNamesBool[] = + false, NULL, NULL + }, + { ++ {"default_with_secids", PGC_USERSET, CONN_AUTH_SETTINGS, ++ gettext_noop("Create new tables with security-ids by default."), ++ NULL ++ }, ++ &default_with_secids, ++ true, NULL, NULL ++ }, ++ { + {"logging_collector", PGC_POSTMASTER, LOGGING_WHERE, + gettext_noop("Start a subprocess to capture stderr output and/or csvlogs into log files."), + NULL +@@ -1261,6 +1284,36 @@ static struct config_bool ConfigureNamesBool[] = + false, NULL, NULL + }, + ++ { ++ {"ignore_security_label_input", PGC_USERSET, CONN_AUTH_SECURITY, ++ gettext_noop("Disables to assign used provided security label"), ++ NULL, ++ GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE ++ }, ++ &ignore_security_label_input, ++ false, NULL, NULL ++ }, ++#ifdef HAVE_SELINUX ++ { ++ {"sepostgresql_mcstrans", PGC_USERSET, CONN_AUTH_SECURITY, ++ gettext_noop("Enables to show security context in human-readable form"), ++ NULL, ++ GUC_NOT_IN_SAMPLE ++ }, ++ &sepgsql_mcstrans, ++ true, NULL, NULL ++ }, ++ { ++ {"sepostgresql_debug_audit", PGC_USERSET, CONN_AUTH_SECURITY, ++ gettext_noop("Enables to show audit logs for debugging"), ++ NULL, ++ GUC_NOT_IN_SAMPLE ++ }, ++ &sepgsql_debug_audit, ++ false, NULL, NULL ++ }, ++#endif ++ + /* End-of-list marker */ + { + {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL +@@ -2820,7 +2873,16 @@ static struct config_enum ConfigureNamesEnum[] = + &xmloption, + XMLOPTION_CONTENT, xmloption_options, NULL, NULL + }, +- ++#ifdef HAVE_SELINUX ++ { ++ {"sepostgresql", PGC_POSTMASTER, CONN_AUTH_SECURITY, ++ gettext_noop("Enables to set SE-PostgreSQL's mode"), ++ NULL ++ }, ++ &sepostgresql_mode, ++ SEPGSQL_MODE_DEFAULT, sepostgresql_options, NULL, sepgsql_show_mode ++ }, ++#endif + + /* End-of-list marker */ + { +@@ -6113,7 +6175,7 @@ GetPGVariableResultDesc(const char *name) + if (guc_name_compare(name, "all") == 0) + { + /* need a tuple descriptor representing three TEXT columns */ +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", +@@ -6129,7 +6191,7 @@ GetPGVariableResultDesc(const char *name) + (void) GetConfigOptionByName(name, &varname); + + /* need a tuple descriptor representing a single TEXT column */ +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, + TEXTOID, -1, 0); + } +@@ -6152,7 +6214,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest) + value = GetConfigOptionByName(name, &varname); + + /* need a tuple descriptor representing a single TEXT column */ +- tupdesc = CreateTemplateTupleDesc(1, false); ++ tupdesc = CreateTemplateTupleDesc(1, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, + TEXTOID, -1, 0); + +@@ -6179,7 +6241,7 @@ ShowAllGUCConfig(DestReceiver *dest) + bool isnull[3] = {false, false, false}; + + /* need a tuple descriptor representing three TEXT columns */ +- tupdesc = CreateTemplateTupleDesc(3, false); ++ tupdesc = CreateTemplateTupleDesc(3, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", +@@ -6574,7 +6636,7 @@ show_all_settings(PG_FUNCTION_ARGS) + * need a tuple descriptor representing NUM_PG_SETTINGS_ATTS columns + * of the appropriate types + */ +- tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false); ++ tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", +diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample +index 2ed8412..10f8947 100644 +--- a/src/backend/utils/misc/postgresql.conf.sample ++++ b/src/backend/utils/misc/postgresql.conf.sample +@@ -76,6 +76,8 @@ + + # - Security and Authentication - + ++#sepostgresql = disabled # default|enforcing|permissive|disabled ++ + #authentication_timeout = 1min # 1s-600s + #ssl = off # (change requires restart) + #ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH' # allowed SSL ciphers +diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c +index ac62d45..f05a094 100644 +--- a/src/backend/utils/mmgr/portalmem.c ++++ b/src/backend/utils/mmgr/portalmem.c +@@ -894,7 +894,7 @@ pg_cursor(PG_FUNCTION_ARGS) + * build tupdesc for result tuples. This must match the definition of the + * pg_cursors view in system_views.sql + */ +- tupdesc = CreateTemplateTupleDesc(6, false); ++ tupdesc = CreateTemplateTupleDesc(6, false, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", +diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c +index f40ad87..497bdf0 100644 +--- a/src/bin/initdb/initdb.c ++++ b/src/bin/initdb/initdb.c +@@ -87,6 +87,7 @@ static bool debug = false; + static bool noclean = false; + static bool show_setting = false; + static char *xlog_dir = ""; ++static bool enable_selinux = false; + + + /* internal vars */ +@@ -1163,6 +1164,13 @@ setup_config(void) + "#default_text_search_config = 'pg_catalog.simple'", + repltok); + ++ if (enable_selinux) ++ { ++ strcpy(repltok, "sepostgresql = default"); ++ conflines = replace_token(conflines, ++ "#sepostgresql = disabled", repltok); ++ } ++ + snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data); + + writefile(path, conflines); +@@ -2394,6 +2402,7 @@ usage(const char *progname) + printf(_(" -U, --username=NAME database superuser name\n")); + printf(_(" -W, --pwprompt prompt for a password for the new superuser\n")); + printf(_(" -X, --xlogdir=XLOGDIR location for the transaction log directory\n")); ++ printf(_(" --enable-selinux enables SELinux support\n")); + printf(_("\nLess commonly used options:\n")); + printf(_(" -d, --debug generate lots of debugging output\n")); + printf(_(" -L DIRECTORY where to find the input files\n")); +@@ -2436,6 +2445,7 @@ main(int argc, char *argv[]) + {"show", no_argument, NULL, 's'}, + {"noclean", no_argument, NULL, 'n'}, + {"xlogdir", required_argument, NULL, 'X'}, ++ {"enable-selinux", no_argument, NULL, 10}, + {NULL, 0, NULL, 0} + }; + +@@ -2545,6 +2555,9 @@ main(int argc, char *argv[]) + case 9: + pwfilename = xstrdup(optarg); + break; ++ case 10: ++ enable_selinux = true; ++ break; + case 's': + show_setting = true; + break; +diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h +index 14fe652..40b7b40 100644 +--- a/src/bin/pg_dump/pg_backup.h ++++ b/src/bin/pg_dump/pg_backup.h +@@ -103,6 +103,7 @@ typedef struct _restoreOptions + * restore */ + int use_setsessauth;/* Use SET SESSION AUTHORIZATION commands + * instead of OWNER TO */ ++ int noSecLabel; /* Don't try to restore security labels */ + char *superuser; /* Username to use as superuser */ + char *use_role; /* Issue SET ROLE to this */ + int dataOnly; +@@ -167,7 +168,7 @@ extern void ArchiveEntry(Archive *AHX, + CatalogId catalogId, DumpId dumpId, + const char *tag, + const char *namespace, const char *tablespace, +- const char *owner, bool withOids, ++ const char *owner, bool withOids, bool withSecids, + const char *desc, teSection section, + const char *defn, + const char *dropStmt, const char *copyStmt, +diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c +index 6bbe2de..9da50b0 100644 +--- a/src/bin/pg_dump/pg_backup_archiver.c ++++ b/src/bin/pg_dump/pg_backup_archiver.c +@@ -540,6 +540,8 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, + } + else + { ++ bool need_reset = false; ++ + _disableTriggersIfNecessary(AH, te, ropt); + + /* Select owner and schema as necessary */ +@@ -575,6 +577,16 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, + "ONLY " : ""), + fmtId(te->tag)); + } ++ /* ++ * If data section has security_label, but pg_restore works ++ * with --no-security-label, we set server flag to ignore ++ * the security label input. ++ */ ++ if (ropt->noSecLabel && te->withSecids) ++ { ++ ahprintf(AH, "SET ignore_security_label_input = on;\n\n"); ++ need_reset = true; ++ } + + /* + * If we have a copy statement, use it. As of V1.3, these +@@ -595,6 +607,9 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, + + AH->writingCopyData = false; + ++ if (need_reset) ++ ahprintf(AH, "RESET ignore_security_label_input;\n\n"); ++ + /* close out the transaction started above */ + if (is_parallel && te->created) + CommitTransaction(AH); +@@ -712,7 +727,7 @@ ArchiveEntry(Archive *AHX, + const char *tag, + const char *namespace, + const char *tablespace, +- const char *owner, bool withOids, ++ const char *owner, bool withOids, bool withSecids, + const char *desc, teSection section, + const char *defn, + const char *dropStmt, const char *copyStmt, +@@ -744,6 +759,7 @@ ArchiveEntry(Archive *AHX, + newToc->tablespace = tablespace ? strdup(tablespace) : NULL; + newToc->owner = strdup(owner); + newToc->withOids = withOids; ++ newToc->withSecids = withSecids; + newToc->desc = strdup(desc); + newToc->defn = strdup(defn); + newToc->dropStmt = strdup(dropStmt); +@@ -2027,6 +2043,7 @@ WriteToc(ArchiveHandle *AH) + WriteStr(AH, te->tablespace); + WriteStr(AH, te->owner); + WriteStr(AH, te->withOids ? "true" : "false"); ++ WriteStr(AH, te->withSecids ? "true" : "false"); + + /* Dump list of dependencies */ + for (i = 0; i < te->nDeps; i++) +@@ -2138,6 +2155,16 @@ ReadToc(ArchiveHandle *AH) + else + te->withOids = true; + ++ if (AH->version >= K_VERS_1_13) ++ { ++ if (strcmp(ReadStr(AH), "true") == 0) ++ te->withSecids = true; ++ else ++ te->withSecids = false; ++ } ++ else ++ te->withSecids = false; ++ + /* Read TOC entry dependencies */ + if (AH->version >= K_VERS_1_5) + { +@@ -2256,6 +2283,9 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) + if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te)) + return 0; + ++ if (ropt->noSecLabel && strcmp(te->desc, "LABEL") == 0) ++ return 0; ++ + /* Ignore DATABASE entry unless we should create it */ + if (!ropt->create && strcmp(te->desc, "DATABASE") == 0) + return 0; +@@ -2322,6 +2352,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) + (strcmp(te->desc, "ACL") == 0 && + strncmp(te->tag, "LARGE OBJECT ", 13) == 0) || + (strcmp(te->desc, "COMMENT") == 0 && ++ strncmp(te->tag, "LARGE OBJECT ", 13) == 0) || ++ (strcmp(te->desc, "LABEL") == 0 && + strncmp(te->tag, "LARGE OBJECT ", 13) == 0)) + res = res & REQ_DATA; + else +@@ -2473,6 +2505,36 @@ _doSetWithOids(ArchiveHandle *AH, const bool withOids) + destroyPQExpBuffer(cmd); + } + ++/* ++ * Issue a SET default_with_secids command. Caller is responsible ++ * for updating state if appropriate. ++ */ ++static void ++_doSetWithSecids(ArchiveHandle *AH, const bool withSecids) ++{ ++ PQExpBuffer cmd = createPQExpBuffer(); ++ ++ appendPQExpBuffer(cmd, "SET default_with_secids = %s;", withSecids ? ++ "true" : "false"); ++ ++ if (RestoringToDB(AH)) ++ { ++ PGresult *res; ++ ++ res = PQexec(AH->connection, cmd->data); ++ ++ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) ++ warn_or_die_horribly(AH, modulename, ++ "could not set default_with_secids: %s", ++ PQerrorMessage(AH->connection)); ++ ++ PQclear(res); ++ } ++ else ++ ahprintf(AH, "%s\n\n", cmd->data); ++ ++ destroyPQExpBuffer(cmd); ++} + + /* + * Issue the commands to connect to the specified database. +@@ -2571,6 +2633,18 @@ _setWithOids(ArchiveHandle *AH, TocEntry *te) + } + } + ++/* ++ * Set the proper default_with_secids value for the table. ++ */ ++static void ++_setWithSecids(ArchiveHandle *AH, TocEntry *te) ++{ ++ if (AH->currWithSecids != te->withSecids) ++ { ++ _doSetWithSecids(AH, te->withSecids); ++ AH->currWithSecids = te->withSecids; ++ } ++} + + /* + * Issue the commands to select the specified schema as the current schema +@@ -2808,9 +2882,12 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat + _selectOutputSchema(AH, te->namespace); + _selectTablespace(AH, te->tablespace); + +- /* Set up OID mode too */ ++ /* Set up OID/SECID mode too */ + if (strcmp(te->desc, "TABLE") == 0) ++ { + _setWithOids(AH, te); ++ _setWithSecids(AH, te); ++ } + + /* Emit header comment for item */ + if (!AH->noTocComments) +diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h +index 2e944c1..67f62ac 100644 +--- a/src/bin/pg_dump/pg_backup_archiver.h ++++ b/src/bin/pg_dump/pg_backup_archiver.h +@@ -63,7 +63,7 @@ typedef z_stream *z_streamp; + + /* Current archive version number (the format we can output) */ + #define K_VERS_MAJOR 1 +-#define K_VERS_MINOR 12 ++#define K_VERS_MINOR 13 + #define K_VERS_REV 0 + + /* Data block types */ +@@ -89,9 +89,10 @@ typedef z_stream *z_streamp; + * indicator */ + #define K_VERS_1_12 (( (1 * 256 + 12) * 256 + 0) * 256 + 0) /* add separate BLOB + * entries */ +- ++#define K_VERS_1_13 (( (1 * 256 + 13) * 256 + 0) * 256 + 0) /* add security label ++ * support */ + /* Newest format we can read */ +-#define K_VERS_MAX (( (1 * 256 + 12) * 256 + 255) * 256 + 0) ++#define K_VERS_MAX (( (1 * 256 + 13) * 256 + 255) * 256 + 0) + + + /* Flags to indicate disposition of offsets stored in files */ +@@ -278,6 +279,7 @@ typedef struct _archiveHandle + char *currSchema; /* current schema, or NULL */ + char *currTablespace; /* current tablespace, or NULL */ + bool currWithOids; /* current default_with_oids setting */ ++ bool currWithSecids; /* current default_with_secids setting */ + + void *lo_buf; + size_t lo_buf_used; +@@ -305,6 +307,7 @@ typedef struct _tocEntry + * means use database default */ + char *owner; + bool withOids; /* Used only by "TABLE" tags */ ++ bool withSecids; /* Used only by "TABLE" tags */ + char *desc; + char *defn; + char *dropStmt; +diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c +index 2431d71..e7ef296 100644 +--- a/src/bin/pg_dump/pg_dump.c ++++ b/src/bin/pg_dump/pg_dump.c +@@ -125,7 +125,7 @@ static int binary_upgrade = 0; + static int disable_dollar_quoting = 0; + static int dump_inserts = 0; + static int column_inserts = 0; +- ++static int security_label = 0; + + static void help(const char *progname); + static void expand_schema_name_patterns(SimpleStringList *patterns, +@@ -183,6 +183,11 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, + const char *tag, const char *nspname, const char *owner, + const char *acls); + ++static void dumpSecLabel(Archive *fout, DumpId objDumpId, ++ const char *target, ++ const char *namespace, ++ const char *seclabel); ++ + static void getDependencies(void); + static void getDomainConstraints(TypeInfo *tyinfo); + static void getTableData(TableInfo *tblinfo, int numTables, bool oids); +@@ -215,7 +220,7 @@ static bool binary_upgrade_set_type_oids_by_rel_oid( + static void binary_upgrade_set_relfilenodes(PQExpBuffer upgrade_buffer, + Oid pg_class_oid, bool is_index); + static const char *getAttrName(int attrnum, TableInfo *tblInfo); +-static const char *fmtCopyColumnList(const TableInfo *ti); ++static const char *fmtCopyColumnList(const TableInfo *ti, bool secids); + static void do_sql_command(PGconn *conn, const char *query); + static void check_sql_result(PGresult *res, PGconn *conn, const char *query, + ExecStatusType expected); +@@ -299,6 +304,7 @@ main(int argc, char **argv) + {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, + {"role", required_argument, NULL, 3}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"security-label", no_argument, &security_label, 1}, + + {NULL, 0, NULL, 0} + }; +@@ -447,6 +453,8 @@ main(int argc, char **argv) + outputNoTablespaces = 1; + else if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; ++ else if (strcmp(optarg, "security-label") == 0) ++ security_label = 1; + else + { + fprintf(stderr, +@@ -515,6 +523,10 @@ main(int argc, char **argv) + exit(1); + } + ++ /* Force column insertion mode, when --security-label mode is given. */ ++ if (security_label && dump_inserts) ++ column_inserts = 1; ++ + /* open the output file */ + if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0) + { +@@ -833,6 +845,7 @@ help(const char *progname) + printf(_(" --use-set-session-authorization\n" + " use SET SESSION AUTHORIZATION commands instead of\n" + " ALTER OWNER commands to set ownership\n")); ++ printf(_(" --security-label dump schema/data with security label\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); +@@ -1127,7 +1140,7 @@ dumpTableData_copy(Archive *fout, void *dcontext) + * cases involving ADD COLUMN and inheritance.) + */ + if (g_fout->remoteVersion >= 70300) +- column_list = fmtCopyColumnList(tbinfo); ++ column_list = fmtCopyColumnList(tbinfo, tdinfo->secids); + else + column_list = ""; /* can't select columns in COPY */ + +@@ -1251,14 +1264,16 @@ dumpTableData_insert(Archive *fout, void *dcontext) + if (fout->remoteVersion >= 70100) + { + appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " +- "SELECT * FROM ONLY %s", ++ "SELECT %s* FROM ONLY %s", ++ (tdinfo->secids ? "security_label," : ""), + fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, + classname)); + } + else + { + appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " +- "SELECT * FROM %s", ++ "SELECT %s* FROM %s", ++ (tdinfo->secids ? "security_label," : ""), + fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, + classname)); + } +@@ -1398,7 +1413,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) + appendPQExpBuffer(copyBuf, "COPY %s ", + fmtId(tbinfo->dobj.name)); + appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n", +- fmtCopyColumnList(tbinfo), ++ fmtCopyColumnList(tbinfo, tdinfo->secids), + (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : ""); + copyStmt = copyBuf->data; + } +@@ -1412,7 +1427,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) + ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId, + tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, + NULL, tbinfo->rolname, +- false, "TABLE DATA", SECTION_DATA, ++ false, tdinfo->secids, "TABLE DATA", SECTION_DATA, + "", "", copyStmt, + tdinfo->dobj.dependencies, tdinfo->dobj.nDeps, + dumpFn, tdinfo); +@@ -1457,6 +1472,7 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids) + tdinfo->dobj.namespace = tblinfo[i].dobj.namespace; + tdinfo->tdtable = &(tblinfo[i]); + tdinfo->oids = oids; ++ tdinfo->secids = (security_label && tblinfo[i].hassecids) ? true : false; + addObjectDependency(&tdinfo->dobj, tblinfo[i].dobj.dumpId); + + tblinfo[i].dataObj = tdinfo; +@@ -1780,6 +1796,7 @@ dumpDatabase(Archive *AH) + NULL, /* Tablespace */ + dba, /* Owner */ + false, /* with oids */ ++ false, /* with secids */ + "DATABASE", /* Desc */ + SECTION_PRE_DATA, /* Section */ + creaQry->data, /* Create */ +@@ -1825,7 +1842,7 @@ dumpDatabase(Archive *AH) + LargeObjectRelationId); + ArchiveEntry(AH, nilCatalogId, createDumpId(), + "pg_largeobject", NULL, NULL, "", +- false, "pg_largeobject", SECTION_PRE_DATA, ++ false, false, "pg_largeobject", SECTION_PRE_DATA, + loOutQry->data, "", NULL, + NULL, 0, + NULL, NULL); +@@ -1857,7 +1874,7 @@ dumpDatabase(Archive *AH) + appendPQExpBuffer(dbQry, ";\n"); + + ArchiveEntry(AH, dbCatId, createDumpId(), datname, NULL, NULL, +- dba, false, "COMMENT", SECTION_NONE, ++ dba, false, false, "COMMENT", SECTION_NONE, + dbQry->data, "", NULL, + &dbDumpId, 1, NULL, NULL); + } +@@ -1896,7 +1913,7 @@ dumpEncoding(Archive *AH) + + ArchiveEntry(AH, nilCatalogId, createDumpId(), + "ENCODING", NULL, NULL, "", +- false, "ENCODING", SECTION_PRE_DATA, ++ false, false, "ENCODING", SECTION_PRE_DATA, + qry->data, "", NULL, + NULL, 0, + NULL, NULL); +@@ -1923,7 +1940,7 @@ dumpStdStrings(Archive *AH) + + ArchiveEntry(AH, nilCatalogId, createDumpId(), + "STDSTRINGS", NULL, NULL, "", +- false, "STDSTRINGS", SECTION_PRE_DATA, ++ false, false, "STDSTRINGS", SECTION_PRE_DATA, + qry->data, "", NULL, + NULL, 0, + NULL, NULL); +@@ -1956,16 +1973,17 @@ getBlobs(Archive *AH) + /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ + if (AH->remoteVersion >= 90000) + appendPQExpBuffer(blobQry, +- "SELECT oid, (%s lomowner) AS rolname, lomacl" ++ "SELECT oid, (%s lomowner) AS rolname, lomacl, %s" + " FROM pg_largeobject_metadata", +- username_subquery); ++ username_subquery, ++ security_label ? "security_label" : "NULL"); + else if (AH->remoteVersion >= 70100) + appendPQExpBuffer(blobQry, +- "SELECT DISTINCT loid, NULL::oid, NULL::oid" ++ "SELECT DISTINCT loid, NULL::oid, NULL::oid, NULL::text" + " FROM pg_largeobject"); + else + appendPQExpBuffer(blobQry, +- "SELECT oid, NULL::oid, NULL::oid" ++ "SELECT oid, NULL::oid, NULL::oid, NULL::text" + " FROM pg_class WHERE relkind = 'l'"); + + res = PQexec(g_conn, blobQry->data); +@@ -1995,6 +2013,11 @@ getBlobs(Archive *AH) + binfo[i].blobacl = strdup(PQgetvalue(res, i, 2)); + else + binfo[i].blobacl = NULL; ++ ++ if (!PQgetisnull(res, i, 3)) ++ binfo[i].seclabel = strdup(PQgetvalue(res, i, 3)); ++ else ++ binfo[i].seclabel = NULL; + } + + /* +@@ -2034,7 +2057,7 @@ dumpBlob(Archive *AH, BlobInfo *binfo) + ArchiveEntry(AH, binfo->dobj.catId, binfo->dobj.dumpId, + binfo->dobj.name, + NULL, NULL, +- binfo->rolname, false, ++ binfo->rolname, false, false, + "BLOB", SECTION_PRE_DATA, + cquery->data, dquery->data, NULL, + binfo->dobj.dependencies, binfo->dobj.nDeps, +@@ -2049,6 +2072,10 @@ dumpBlob(Archive *AH, BlobInfo *binfo) + NULL, binfo->rolname, + binfo->dobj.catId, 0, binfo->dobj.dumpId); + ++ /* Dump security label if any */ ++ dumpSecLabel(AH, binfo->dobj.dumpId, ++ cquery->data, NULL, binfo->seclabel); ++ + /* Dump ACL if any */ + if (binfo->blobacl) + dumpACL(AH, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT", +@@ -2356,6 +2383,7 @@ getNamespaces(int *numNamespaces) + int i_nspname; + int i_rolname; + int i_nspacl; ++ int i_seclabel; + + /* + * Before 7.3, there are no real namespaces; create two dummy entries, one +@@ -2372,6 +2400,7 @@ getNamespaces(int *numNamespaces) + nsinfo[0].dobj.name = strdup("public"); + nsinfo[0].rolname = strdup(""); + nsinfo[0].nspacl = strdup(""); ++ nsinfo[0].seclabel = strdup(""); + + selectDumpableNamespace(&nsinfo[0]); + +@@ -2382,6 +2411,7 @@ getNamespaces(int *numNamespaces) + nsinfo[1].dobj.name = strdup("pg_catalog"); + nsinfo[1].rolname = strdup(""); + nsinfo[1].nspacl = strdup(""); ++ nsinfo[1].seclabel = strdup(""); + + selectDumpableNamespace(&nsinfo[1]); + +@@ -2402,8 +2432,8 @@ getNamespaces(int *numNamespaces) + */ + appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, " + "(%s nspowner) AS rolname, " +- "nspacl FROM pg_namespace", +- username_subquery); ++ "nspacl, %s AS seclabel FROM pg_namespace", ++ username_subquery, security_label ? "security_label" : "NULL"); + + res = PQexec(g_conn, query->data); + check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); +@@ -2417,6 +2447,7 @@ getNamespaces(int *numNamespaces) + i_nspname = PQfnumber(res, "nspname"); + i_rolname = PQfnumber(res, "rolname"); + i_nspacl = PQfnumber(res, "nspacl"); ++ i_seclabel = PQfnumber(res, "seclabel"); + + for (i = 0; i < ntups; i++) + { +@@ -2427,6 +2458,7 @@ getNamespaces(int *numNamespaces) + nsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_nspname)); + nsinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname)); + nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl)); ++ nsinfo[i].seclabel = strdup(PQgetvalue(res, i, i_seclabel)); + + /* Decide whether to dump this namespace */ + selectDumpableNamespace(&nsinfo[i]); +@@ -2515,6 +2547,7 @@ getTypes(int *numTypes) + int i_typtype; + int i_typisdefined; + int i_isarray; ++ int i_seclabel; + + /* + * we include even the built-in types because those may be used as array +@@ -2547,8 +2580,10 @@ getTypes(int *numTypes) + "typtype, typisdefined, " + "typname[0] = '_' AND typelem != 0 AND " + "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray " ++ ",%s AS seclabel " + "FROM pg_type", +- username_subquery); ++ username_subquery, ++ security_label ? "security_label" : "NULL"); + } + else if (g_fout->remoteVersion >= 70300) + { +@@ -2561,6 +2596,7 @@ getTypes(int *numTypes) + "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " + "typtype, typisdefined, " + "typname[0] = '_' AND typelem != 0 AS isarray " ++ ",NULL AS seclabel " + "FROM pg_type", + username_subquery); + } +@@ -2575,6 +2611,7 @@ getTypes(int *numTypes) + "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " + "typtype, typisdefined, " + "typname[0] = '_' AND typelem != 0 AS isarray " ++ ",NULL AS seclabel " + "FROM pg_type", + username_subquery); + } +@@ -2591,6 +2628,7 @@ getTypes(int *numTypes) + "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " + "typtype, typisdefined, " + "typname[0] = '_' AND typelem != 0 AS isarray " ++ ",NULL AS seclabel " + "FROM pg_type", + username_subquery); + } +@@ -2615,6 +2653,7 @@ getTypes(int *numTypes) + i_typtype = PQfnumber(res, "typtype"); + i_typisdefined = PQfnumber(res, "typisdefined"); + i_isarray = PQfnumber(res, "isarray"); ++ i_seclabel = PQfnumber(res, "seclabel"); + + for (i = 0; i < ntups; i++) + { +@@ -2642,6 +2681,8 @@ getTypes(int *numTypes) + else + tyinfo[i].isArray = false; + ++ tyinfo[i].seclabel = strdup(PQgetvalue(res, i, i_seclabel)); ++ + /* Decide whether we want to dump it */ + selectDumpableType(&tyinfo[i]); + +@@ -3407,6 +3448,7 @@ getTables(int *numTables) + int i_relhasindex; + int i_relhasrules; + int i_relhasoids; ++ int i_relhassecids; + int i_relfrozenxid; + int i_owning_tab; + int i_owning_col; +@@ -3414,6 +3456,7 @@ getTables(int *numTables) + int i_reloptions; + int i_toastreloptions; + int i_reloftype; ++ int i_seclabel; + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); +@@ -3450,6 +3493,7 @@ getTables(int *numTables) + "(%s c.relowner) AS rolname, " + "c.relchecks, c.relhastriggers, " + "c.relhasindex, c.relhasrules, c.relhasoids, " ++ "c.relhassecids, " + "c.relfrozenxid, " + "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " + "d.refobjid AS owning_tab, " +@@ -3457,6 +3501,7 @@ getTables(int *numTables) + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " + "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " ++ ",%s AS seclabel " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3467,6 +3512,7 @@ getTables(int *numTables) + "WHERE c.relkind in ('%c', '%c', '%c', '%c') " + "ORDER BY c.oid", + username_subquery, ++ security_label ? "c.security_label" : "NULL", + RELKIND_SEQUENCE, + RELKIND_RELATION, RELKIND_SEQUENCE, + RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); +@@ -3483,6 +3529,7 @@ getTables(int *numTables) + "(%s c.relowner) AS rolname, " + "c.relchecks, c.relhastriggers, " + "c.relhasindex, c.relhasrules, c.relhasoids, " ++ "false AS relhasecids, " + "c.relfrozenxid, " + "NULL AS reloftype, " + "d.refobjid AS owning_tab, " +@@ -3490,6 +3537,7 @@ getTables(int *numTables) + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " + "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " ++ ",%s AS seclabel " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3500,6 +3548,7 @@ getTables(int *numTables) + "WHERE c.relkind in ('%c', '%c', '%c', '%c') " + "ORDER BY c.oid", + username_subquery, ++ security_label ? "security_label" : "NULL", + RELKIND_SEQUENCE, + RELKIND_RELATION, RELKIND_SEQUENCE, + RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); +@@ -3516,6 +3565,7 @@ getTables(int *numTables) + "(%s relowner) AS rolname, " + "relchecks, (reltriggers <> 0) AS relhastriggers, " + "relhasindex, relhasrules, relhasoids, " ++ "false AS relhassecids, " + "relfrozenxid, " + "NULL AS reloftype, " + "d.refobjid AS owning_tab, " +@@ -3523,6 +3573,7 @@ getTables(int *numTables) + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " + "NULL AS toast_reloptions " ++ ",NULL AS seclabel " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3548,6 +3599,7 @@ getTables(int *numTables) + "(%s relowner) AS rolname, " + "relchecks, (reltriggers <> 0) AS relhastriggers, " + "relhasindex, relhasrules, relhasoids, " ++ "false AS relhassecids, " + "0 AS relfrozenxid, " + "NULL AS reloftype, " + "d.refobjid AS owning_tab, " +@@ -3555,6 +3607,7 @@ getTables(int *numTables) + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "NULL AS reloptions, " + "NULL AS toast_reloptions " ++ ",NULL AS seclabel " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3580,6 +3633,7 @@ getTables(int *numTables) + "(%s relowner) AS rolname, " + "relchecks, (reltriggers <> 0) AS relhastriggers, " + "relhasindex, relhasrules, relhasoids, " ++ "false AS relhassecids, " + "0 AS relfrozenxid, " + "NULL AS reloftype, " + "d.refobjid AS owning_tab, " +@@ -3587,6 +3641,7 @@ getTables(int *numTables) + "NULL AS reltablespace, " + "NULL AS reloptions, " + "NULL AS toast_reloptions " ++ ",NULL AS seclabel " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +@@ -3608,6 +3663,7 @@ getTables(int *numTables) + "(%s relowner) AS rolname, " + "relchecks, (reltriggers <> 0) AS relhastriggers, " + "relhasindex, relhasrules, relhasoids, " ++ "false AS relhassecids, " + "0 AS relfrozenxid, " + "NULL AS reloftype, " + "NULL::oid AS owning_tab, " +@@ -3615,6 +3671,7 @@ getTables(int *numTables) + "NULL AS reltablespace, " + "NULL AS reloptions, " + "NULL AS toast_reloptions " ++ ",NULL AS seclabel " + "FROM pg_class " + "WHERE relkind IN ('%c', '%c', '%c') " + "ORDER BY oid", +@@ -3631,6 +3688,7 @@ getTables(int *numTables) + "relchecks, (reltriggers <> 0) AS relhastriggers, " + "relhasindex, relhasrules, " + "'t'::bool AS relhasoids, " ++ "'f'::bool AS relhassecids, " + "0 AS relfrozenxid, " + "NULL AS reloftype, " + "NULL::oid AS owning_tab, " +@@ -3638,6 +3696,7 @@ getTables(int *numTables) + "NULL AS reltablespace, " + "NULL AS reloptions, " + "NULL AS toast_reloptions " ++ ",NULL AS seclabel " + "FROM pg_class " + "WHERE relkind IN ('%c', '%c', '%c') " + "ORDER BY oid", +@@ -3671,6 +3730,7 @@ getTables(int *numTables) + "NULL AS reltablespace, " + "NULL AS reloptions, " + "NULL AS toast_reloptions " ++ ",NULL AS seclabel " + "FROM pg_class c " + "WHERE relkind IN ('%c', '%c') " + "ORDER BY oid", +@@ -3709,6 +3769,7 @@ getTables(int *numTables) + i_relhasindex = PQfnumber(res, "relhasindex"); + i_relhasrules = PQfnumber(res, "relhasrules"); + i_relhasoids = PQfnumber(res, "relhasoids"); ++ i_relhassecids = PQfnumber(res, "relhassecids"); + i_relfrozenxid = PQfnumber(res, "relfrozenxid"); + i_owning_tab = PQfnumber(res, "owning_tab"); + i_owning_col = PQfnumber(res, "owning_col"); +@@ -3716,6 +3777,7 @@ getTables(int *numTables) + i_reloptions = PQfnumber(res, "reloptions"); + i_toastreloptions = PQfnumber(res, "toast_reloptions"); + i_reloftype = PQfnumber(res, "reloftype"); ++ i_seclabel = PQfnumber(res, "seclabel"); + + if (lockWaitTimeout && g_fout->remoteVersion >= 70300) + { +@@ -3748,6 +3810,7 @@ getTables(int *numTables) + tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0); + tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0); + tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0); ++ tblinfo[i].hassecids = (strcmp(PQgetvalue(res, i, i_relhassecids), "t")==0); + tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid)); + if (PQgetisnull(res, i, i_reloftype)) + tblinfo[i].reloftype = NULL; +@@ -3767,6 +3830,7 @@ getTables(int *numTables) + tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace)); + tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions)); + tblinfo[i].toast_reloptions = strdup(PQgetvalue(res, i, i_toastreloptions)); ++ tblinfo[i].rellabel = strdup(PQgetvalue(res, i, i_seclabel)); + + /* other fields were zeroed above */ + +@@ -5082,6 +5146,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + int i_attalign; + int i_attislocal; + int i_attoptions; ++ int i_seclabel; + PGresult *res; + int ntups; + bool hasdefaults; +@@ -5128,11 +5193,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + "a.attlen, a.attalign, a.attislocal, " + "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " + "array_to_string(attoptions, ', ') AS attoptions " ++ ",%s AS seclabel " + "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::pg_catalog.oid " + "AND a.attnum > 0::pg_catalog.int2 " + "ORDER BY a.attrelid, a.attnum", ++ security_label ? "a.security_label" : "NULL", + tbinfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 70300) +@@ -5144,6 +5211,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + "a.attlen, a.attalign, a.attislocal, " + "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " + "'' AS attoptions " ++ ",NULL AS seclabel " + "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::pg_catalog.oid " +@@ -5165,6 +5233,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + "a.attalign, false AS attislocal, " + "format_type(t.oid,a.atttypmod) AS atttypname, " + "'' AS attoptions " ++ ",NULL AS seclabel " + "FROM pg_attribute a LEFT JOIN pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::oid " +@@ -5183,6 +5252,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + "false AS attislocal, " + "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, " + "'' AS attoptions " ++ ",NULL AS seclabel " + "FROM pg_attribute a " + "WHERE attrelid = '%u'::oid " + "AND attnum > 0::int2 " +@@ -5209,6 +5279,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + i_attalign = PQfnumber(res, "attalign"); + i_attislocal = PQfnumber(res, "attislocal"); + i_attoptions = PQfnumber(res, "attoptions"); ++ i_seclabel = PQfnumber(res, "seclabel"); + + tbinfo->numatts = ntups; + tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); +@@ -5227,6 +5298,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); + tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool)); + tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool)); ++ tbinfo->attlabels = (char **) malloc(ntups * sizeof(char *)); + hasdefaults = false; + + for (j = 0; j < ntups; j++) +@@ -5256,6 +5328,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) + tbinfo->inhAttrs[j] = false; + tbinfo->inhAttrDef[j] = false; + tbinfo->inhNotNull[j] = false; ++ tbinfo->attlabels[j] = strdup(PQgetvalue(res, j, i_seclabel)); + } + + PQclear(res); +@@ -6146,7 +6219,7 @@ dumpComment(Archive *fout, const char *target, + */ + ArchiveEntry(fout, nilCatalogId, createDumpId(), + target, namespace, NULL, owner, +- false, "COMMENT", SECTION_NONE, ++ false, false, "COMMENT", SECTION_NONE, + query->data, "", NULL, + &(dumpId), 1, + NULL, NULL); +@@ -6207,7 +6280,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, + target->data, + tbinfo->dobj.namespace->dobj.name, + NULL, tbinfo->rolname, +- false, "COMMENT", SECTION_NONE, ++ false, false, "COMMENT", SECTION_NONE, + query->data, "", NULL, + &(tbinfo->dobj.dumpId), 1, + NULL, NULL); +@@ -6229,7 +6302,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, + target->data, + tbinfo->dobj.namespace->dobj.name, + NULL, tbinfo->rolname, +- false, "COMMENT", SECTION_NONE, ++ false, false, "COMMENT", SECTION_NONE, + query->data, "", NULL, + &(tbinfo->dobj.dumpId), 1, + NULL, NULL); +@@ -6509,7 +6582,7 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) + case DO_BLOB_DATA: + ArchiveEntry(fout, dobj->catId, dobj->dumpId, + dobj->name, NULL, NULL, "", +- false, "BLOBS", SECTION_DATA, ++ false, false, "BLOBS", SECTION_DATA, + "", "", NULL, + dobj->dependencies, dobj->nDeps, + dumpBlobs, NULL); +@@ -6549,7 +6622,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) + nspinfo->dobj.name, + NULL, NULL, + nspinfo->rolname, +- false, "SCHEMA", SECTION_PRE_DATA, ++ false, false, "SCHEMA", SECTION_PRE_DATA, + q->data, delq->data, NULL, + nspinfo->dobj.dependencies, nspinfo->dobj.nDeps, + NULL, NULL); +@@ -6561,6 +6634,9 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) + NULL, nspinfo->rolname, + nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); + ++ dumpSecLabel(fout, nspinfo->dobj.dumpId, ++ q->data, NULL, nspinfo->seclabel); ++ + dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA", + qnspname, NULL, nspinfo->dobj.name, NULL, + nspinfo->rolname, nspinfo->nspacl); +@@ -6678,7 +6754,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + NULL, +- tyinfo->rolname, false, ++ tyinfo->rolname, false, false, + "TYPE", SECTION_PRE_DATA, + q->data, delq->data, NULL, + tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, +@@ -6692,6 +6768,12 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + ++ /* Dump type security label */ ++ dumpSecLabel(fout, tyinfo->dobj.dumpId, ++ q->data, ++ tyinfo->dobj.namespace->dobj.name, ++ tyinfo->seclabel); ++ + PQclear(res); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); +@@ -7054,7 +7136,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + NULL, +- tyinfo->rolname, false, ++ tyinfo->rolname, false, false, + "TYPE", SECTION_PRE_DATA, + q->data, delq->data, NULL, + tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, +@@ -7068,6 +7150,12 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + ++ /* Dump type security label */ ++ dumpSecLabel(fout, tyinfo->dobj.dumpId, ++ q->data, ++ tyinfo->dobj.namespace->dobj.name, ++ tyinfo->seclabel); ++ + PQclear(res); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); +@@ -7178,7 +7266,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + NULL, +- tyinfo->rolname, false, ++ tyinfo->rolname, false, false, + "DOMAIN", SECTION_PRE_DATA, + q->data, delq->data, NULL, + tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, +@@ -7192,6 +7280,12 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + ++ /* Dump domain security label */ ++ dumpSecLabel(fout, tyinfo->dobj.dumpId, ++ q->data, ++ tyinfo->dobj.namespace->dobj.name, ++ tyinfo->seclabel); ++ + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); + destroyPQExpBuffer(query); +@@ -7283,7 +7377,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + NULL, +- tyinfo->rolname, false, ++ tyinfo->rolname, false, false, + "TYPE", SECTION_PRE_DATA, + q->data, delq->data, NULL, + tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, +@@ -7298,6 +7392,12 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) + tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, + tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + ++ /* Dump type security label */ ++ dumpSecLabel(fout, tyinfo->dobj.dumpId, ++ q->data, ++ tyinfo->dobj.namespace->dobj.name, ++ tyinfo->seclabel); ++ + PQclear(res); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); +@@ -7402,7 +7502,7 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo) + target->data, + tyinfo->dobj.namespace->dobj.name, + NULL, tyinfo->rolname, +- false, "COMMENT", SECTION_NONE, ++ false, false, "COMMENT", SECTION_NONE, + query->data, "", NULL, + &(tyinfo->dobj.dumpId), 1, + NULL, NULL); +@@ -7454,7 +7554,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo) + stinfo->dobj.name, + stinfo->dobj.namespace->dobj.name, + NULL, +- stinfo->baseType->rolname, false, ++ stinfo->baseType->rolname, false, false, + "SHELL TYPE", SECTION_PRE_DATA, + q->data, "", NULL, + stinfo->dobj.dependencies, stinfo->dobj.nDeps, +@@ -7609,7 +7709,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) + ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId, + plang->dobj.name, + lanschema, NULL, plang->lanowner, +- false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA, ++ false, false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA, + defqry->data, delqry->data, NULL, + plang->dobj.dependencies, plang->dobj.nDeps, + NULL, NULL); +@@ -7795,6 +7895,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + char **argmodes = NULL; + char **argnames = NULL; + char **configitems = NULL; ++ char *proseclabel; + int nconfigitems = 0; + int i; + +@@ -7825,8 +7926,10 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "proiswindow, provolatile, proisstrict, prosecdef, " + "proconfig, procost, prorows, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ ",%s AS seclabel " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", ++ security_label ? "security_label" : "NULL", + finfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 80300) +@@ -7838,6 +7941,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "provolatile, proisstrict, prosecdef, " + "proconfig, procost, prorows, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ ",NULL AS seclabel " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +@@ -7851,6 +7955,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ ",NULL AS seclabel " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +@@ -7866,6 +7971,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ ",NULL AS seclabel " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +@@ -7881,6 +7987,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " ++ ",NULL AS seclabel " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +@@ -7898,6 +8005,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "false AS prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " + "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " ++ ",NULL AS seclabel " + "FROM pg_proc " + "WHERE oid = '%u'::oid", + finfo->dobj.catId.oid); +@@ -7915,6 +8023,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + "false AS prosecdef, " + "NULL AS proconfig, 0 AS procost, 0 AS prorows, " + "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " ++ ",NULL AS seclabel " + "FROM pg_proc " + "WHERE oid = '%u'::oid", + finfo->dobj.catId.oid); +@@ -7959,6 +8068,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + procost = PQgetvalue(res, 0, PQfnumber(res, "procost")); + prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); + lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); ++ proseclabel = PQgetvalue(res, 0, PQfnumber(res, "seclabel")); + + /* + * See backend/commands/functioncmds.c for details of how the 'AS' clause +@@ -8169,7 +8279,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + funcsig_tag, + finfo->dobj.namespace->dobj.name, + NULL, +- finfo->rolname, false, ++ finfo->rolname, false, false, + "FUNCTION", SECTION_PRE_DATA, + q->data, delqry->data, NULL, + finfo->dobj.dependencies, finfo->dobj.nDeps, +@@ -8182,6 +8292,12 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + finfo->dobj.namespace->dobj.name, finfo->rolname, + finfo->dobj.catId, 0, finfo->dobj.dumpId); + ++ /* Dump Function security label */ ++ dumpSecLabel(fout, finfo->dobj.dumpId, ++ q->data, ++ finfo->dobj.namespace->dobj.name, ++ proseclabel); ++ + dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION", + funcsig, NULL, funcsig_tag, + finfo->dobj.namespace->dobj.name, +@@ -8323,7 +8439,7 @@ dumpCast(Archive *fout, CastInfo *cast) + ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId, + castsig->data, + "pg_catalog", NULL, "", +- false, "CAST", SECTION_PRE_DATA, ++ false, false, "CAST", SECTION_PRE_DATA, + defqry->data, delqry->data, NULL, + cast->dobj.dependencies, cast->dobj.nDeps, + NULL, NULL); +@@ -8567,7 +8683,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) + oprinfo->dobj.namespace->dobj.name, + NULL, + oprinfo->rolname, +- false, "OPERATOR", SECTION_PRE_DATA, ++ false, false, "OPERATOR", SECTION_PRE_DATA, + q->data, delq->data, NULL, + oprinfo->dobj.dependencies, oprinfo->dobj.nDeps, + NULL, NULL); +@@ -9026,7 +9142,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) + opcinfo->dobj.namespace->dobj.name, + NULL, + opcinfo->rolname, +- false, "OPERATOR CLASS", SECTION_PRE_DATA, ++ false, false, "OPERATOR CLASS", SECTION_PRE_DATA, + q->data, delq->data, NULL, + opcinfo->dobj.dependencies, opcinfo->dobj.nDeps, + NULL, NULL); +@@ -9307,7 +9423,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) + opfinfo->dobj.namespace->dobj.name, + NULL, + opfinfo->rolname, +- false, "OPERATOR FAMILY", SECTION_PRE_DATA, ++ false, false, "OPERATOR FAMILY", SECTION_PRE_DATA, + q->data, delq->data, NULL, + opfinfo->dobj.dependencies, opfinfo->dobj.nDeps, + NULL, NULL); +@@ -9423,7 +9539,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) + convinfo->dobj.namespace->dobj.name, + NULL, + convinfo->rolname, +- false, "CONVERSION", SECTION_PRE_DATA, ++ false, false, "CONVERSION", SECTION_PRE_DATA, + q->data, delq->data, NULL, + convinfo->dobj.dependencies, convinfo->dobj.nDeps, + NULL, NULL); +@@ -9504,11 +9620,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + int i_aggtranstype; + int i_agginitval; + int i_convertok; ++ int i_seclabel; + const char *aggtransfn; + const char *aggfinalfn; + const char *aggsortop; + const char *aggtranstype; + const char *agginitval; ++ const char *seclabel; + bool convertok; + + /* Skip if not to be dumped */ +@@ -9531,9 +9649,11 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + "aggsortop::pg_catalog.regoperator, " + "agginitval, " + "'t'::boolean AS convertok " ++ ",%s AS seclabel " + "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " + "WHERE a.aggfnoid = p.oid " + "AND p.oid = '%u'::pg_catalog.oid", ++ security_label ? "p.security_label" : "NULL", + agginfo->aggfn.dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 70300) +@@ -9543,6 +9663,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + "0 AS aggsortop, " + "agginitval, " + "'t'::boolean AS convertok " ++ ",NULL AS seclabel " + "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " + "WHERE a.aggfnoid = p.oid " + "AND p.oid = '%u'::pg_catalog.oid", +@@ -9555,6 +9676,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + "0 AS aggsortop, " + "agginitval, " + "'t'::boolean AS convertok " ++ ",NULL AS seclabel " + "FROM pg_aggregate " + "WHERE oid = '%u'::oid", + agginfo->aggfn.dobj.catId.oid); +@@ -9567,6 +9689,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + "0 AS aggsortop, " + "agginitval1 AS agginitval, " + "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok " ++ ",NULL AS seclabel " + "FROM pg_aggregate " + "WHERE oid = '%u'::oid", + agginfo->aggfn.dobj.catId.oid); +@@ -9592,6 +9715,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + i_aggtranstype = PQfnumber(res, "aggtranstype"); + i_agginitval = PQfnumber(res, "agginitval"); + i_convertok = PQfnumber(res, "convertok"); ++ i_seclabel = PQfnumber(res, "seclabel"); + + aggtransfn = PQgetvalue(res, 0, i_aggtransfn); + aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn); +@@ -9599,6 +9723,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + aggtranstype = PQgetvalue(res, 0, i_aggtranstype); + agginitval = PQgetvalue(res, 0, i_agginitval); + convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't'); ++ seclabel = PQgetvalue(res, 0, i_seclabel); + + aggsig = format_aggregate_signature(agginfo, fout, true); + aggsig_tag = format_aggregate_signature(agginfo, fout, false); +@@ -9667,7 +9792,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + agginfo->aggfn.dobj.namespace->dobj.name, + NULL, + agginfo->aggfn.rolname, +- false, "AGGREGATE", SECTION_PRE_DATA, ++ false, false, "AGGREGATE", SECTION_PRE_DATA, + q->data, delq->data, NULL, + agginfo->aggfn.dobj.dependencies, agginfo->aggfn.dobj.nDeps, + NULL, NULL); +@@ -9679,6 +9804,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo) + agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname, + agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId); + ++ /* Dump Aggregate security label */ ++ dumpSecLabel(fout, agginfo->aggfn.dobj.dumpId, ++ q->data, ++ agginfo->aggfn.dobj.namespace->dobj.name, ++ seclabel); ++ + /* + * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL + * command look like a function's GRANT; in particular this affects the +@@ -9755,7 +9886,7 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) + prsinfo->dobj.namespace->dobj.name, + NULL, + "", +- false, "TEXT SEARCH PARSER", SECTION_PRE_DATA, ++ false, false, "TEXT SEARCH PARSER", SECTION_PRE_DATA, + q->data, delq->data, NULL, + prsinfo->dobj.dependencies, prsinfo->dobj.nDeps, + NULL, NULL); +@@ -9847,7 +9978,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) + dictinfo->dobj.namespace->dobj.name, + NULL, + dictinfo->rolname, +- false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA, ++ false, false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA, + q->data, delq->data, NULL, + dictinfo->dobj.dependencies, dictinfo->dobj.nDeps, + NULL, NULL); +@@ -9907,7 +10038,7 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) + tmplinfo->dobj.namespace->dobj.name, + NULL, + "", +- false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA, ++ false, false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA, + q->data, delq->data, NULL, + tmplinfo->dobj.dependencies, tmplinfo->dobj.nDeps, + NULL, NULL); +@@ -10040,7 +10171,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) + cfginfo->dobj.namespace->dobj.name, + NULL, + cfginfo->rolname, +- false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA, ++ false, false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA, + q->data, delq->data, NULL, + cfginfo->dobj.dependencies, cfginfo->dobj.nDeps, + NULL, NULL); +@@ -10096,7 +10227,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) + NULL, + NULL, + fdwinfo->rolname, +- false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA, ++ false, false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA, + q->data, delq->data, NULL, + fdwinfo->dobj.dependencies, fdwinfo->dobj.nDeps, + NULL, NULL); +@@ -10183,7 +10314,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) + NULL, + NULL, + srvinfo->rolname, +- false, "SERVER", SECTION_PRE_DATA, ++ false, false, "SERVER", SECTION_PRE_DATA, + q->data, delq->data, NULL, + srvinfo->dobj.dependencies, srvinfo->dobj.nDeps, + NULL, NULL); +@@ -10279,7 +10410,7 @@ dumpUserMappings(Archive *fout, const char *target, + tag->data, + namespace, + NULL, +- owner, false, ++ owner, false, false, + "USER MAPPING", SECTION_PRE_DATA, + q->data, delq->data, NULL, + &dumpId, 1, +@@ -10350,7 +10481,7 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) + daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL, + NULL, + daclinfo->defaclrole, +- false, "DEFAULT ACL", SECTION_NONE, ++ false, false, "DEFAULT ACL", SECTION_NONE, + q->data, "", NULL, + daclinfo->dobj.dependencies, daclinfo->dobj.nDeps, + NULL, NULL); +@@ -10407,7 +10538,7 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, + tag, nspname, + NULL, + owner ? owner : "", +- false, "ACL", SECTION_NONE, ++ false, false, "ACL", SECTION_NONE, + sql->data, "", NULL, + &(objDumpId), 1, + NULL, NULL); +@@ -10416,6 +10547,123 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, + } + + /* ++ * dumpSecLabel ++ * ++ * write out security label of the objects ++ */ ++static void ++dumpSecLabel(Archive *fout, DumpId objDumpId, ++ const char *target, const char *namespace, ++ const char *seclabel) ++{ ++ PQExpBuffer qry; ++ ++ /* Do nothing, if security label dump is not given */ ++ if (!security_label || strlen(seclabel) == 0) ++ return; ++ ++ /* Do nothing, if --data-only for schemas, or --schema-only for blobs */ ++ if (strncmp(target, "LARGE OBJECT", 13) == 0) ++ { ++ if (schemaOnly) ++ return; ++ } ++ else ++ { ++ if (dataOnly) ++ return; ++ } ++ ++ /* Make ALTER xxx SECURITY LABEL TO command */ ++ qry = createPQExpBuffer(); ++ ++ appendPQExpBuffer(qry, "ALTER %s SECURITY LABEL TO '%s';", ++ target, seclabel); ++ ++ ArchiveEntry(fout, ++ nilCatalogId, /* catalog ID */ ++ createDumpId(), /* dump ID */ ++ target, /* name */ ++ namespace, /* namespace */ ++ NULL, /* tablespace */ ++ "", /* owner */ ++ false, /* with oids */ ++ false, /* with secids */ ++ "LABEL", /* desc */ ++ SECTION_NONE, /* section */ ++ qry->data, /* create */ ++ "", /* delete*/ ++ NULL, /* copy */ ++ &(objDumpId), /* dependency */ ++ 1, /* # deps */ ++ NULL, /* dumper Func */ ++ NULL); /* dumper Arg */ ++ ++ destroyPQExpBuffer(qry); ++} ++ ++/* ++ * dumpTableSecLabel ++ * ++ * write out security label of the table ++ */ ++static void ++dumpTableSecLabel(Archive *fout, DumpId objDumpId, ++ const char *namespace, ++ const char *table_name, ++ const char **column_names, int ncolumns, ++ const char *table_label, const char **column_labels) ++{ ++ PQExpBuffer qry; ++ PQExpBuffer tag; ++ int i; ++ ++ if (!security_label || dataOnly) ++ return; ++ ++ /* Make ALTER xxx SECURITY LABEL TO command */ ++ qry = createPQExpBuffer(); ++ tag = createPQExpBuffer(); ++ ++ appendPQExpBuffer(tag, "TABLE %s", table_name); ++ ++ if (strlen(table_label) > 0) ++ appendPQExpBuffer(qry, "ALTER TABLE %s SECURITY LABEL TO '%s';\n", ++ table_name, table_label); ++ for (i = 0; i < ncolumns; i++) ++ { ++ if (strlen(column_labels[i]) > 0) ++ appendPQExpBuffer(qry, "ALTER TABLE %s ALTER %s SECURITY LABEL TO '%s';\n", ++ table_name, ++ column_names[i], ++ column_labels[i]); ++ } ++ ++ if (qry->len > 0) ++ ArchiveEntry(fout, ++ nilCatalogId, /* catalog ID */ ++ createDumpId(), /* dump ID */ ++ tag->data, /* name */ ++ namespace, /* namespace */ ++ NULL, /* tablespace */ ++ "", /* owner */ ++ false, /* with oids */ ++ false, /* with secids */ ++ "LABEL", /* desc */ ++ SECTION_NONE, /* section */ ++ qry->data, /* create */ ++ "", /* delete*/ ++ NULL, /* copy */ ++ &(objDumpId), /* dependency */ ++ 1, /* # deps */ ++ NULL, /* dumper Func */ ++ NULL); /* dumper Arg */ ++ ++ destroyPQExpBuffer(qry); ++ destroyPQExpBuffer(tag); ++} ++ ++/* + * dumpTable + * write out to fout the declarations (not data) of a user-defined table + */ +@@ -10571,7 +10819,6 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) + + appendPQExpBuffer(q, "CREATE VIEW %s AS\n %s\n", + fmtId(tbinfo->dobj.name), viewdef); +- + PQclear(res); + } + else +@@ -10922,6 +11169,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) + (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace, + tbinfo->rolname, + (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false, ++ (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hassecids : false, + reltypename, SECTION_PRE_DATA, + q->data, delq->data, NULL, + tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, +@@ -10942,6 +11190,27 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) + dumpTableConstraintComment(fout, constr); + } + ++ /* Dump relation/attribute's security labels */ ++ if (tbinfo->relkind == RELKIND_RELATION) ++ { ++ dumpTableSecLabel(fout, tbinfo->dobj.dumpId, ++ tbinfo->dobj.namespace->dobj.name, ++ tbinfo->dobj.name, ++ tbinfo->attnames, tbinfo->numatts, ++ tbinfo->rellabel, tbinfo->attlabels); ++ } ++ else ++ { ++ PQExpBuffer target = createPQExpBuffer(); ++ ++ appendPQExpBuffer(target, "%s %s", reltypename, fmtId(tbinfo->dobj.name)); ++ dumpSecLabel(fout, tbinfo->dobj.dumpId, ++ target->data, ++ tbinfo->dobj.namespace->dobj.name, ++ tbinfo->rellabel); ++ destroyPQExpBuffer(target); ++ } ++ + destroyPQExpBuffer(query); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); +@@ -10990,7 +11259,7 @@ dumpAttrDef(Archive *fout, AttrDefInfo *adinfo) + tbinfo->dobj.namespace->dobj.name, + NULL, + tbinfo->rolname, +- false, "DEFAULT", SECTION_PRE_DATA, ++ false, false, "DEFAULT", SECTION_PRE_DATA, + q->data, delq->data, NULL, + adinfo->dobj.dependencies, adinfo->dobj.nDeps, + NULL, NULL); +@@ -11086,7 +11355,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) + indxinfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + indxinfo->tablespace, +- tbinfo->rolname, false, ++ tbinfo->rolname, false, false, + "INDEX", SECTION_POST_DATA, + q->data, delq->data, NULL, + indxinfo->dobj.dependencies, indxinfo->dobj.nDeps, +@@ -11211,7 +11480,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) + coninfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + indxinfo->tablespace, +- tbinfo->rolname, false, ++ tbinfo->rolname, false, false, + "CONSTRAINT", SECTION_POST_DATA, + q->data, delq->data, NULL, + coninfo->dobj.dependencies, coninfo->dobj.nDeps, +@@ -11244,7 +11513,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) + coninfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + NULL, +- tbinfo->rolname, false, ++ tbinfo->rolname, false, false, + "FK CONSTRAINT", SECTION_POST_DATA, + q->data, delq->data, NULL, + coninfo->dobj.dependencies, coninfo->dobj.nDeps, +@@ -11279,7 +11548,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) + coninfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + NULL, +- tbinfo->rolname, false, ++ tbinfo->rolname, false, false, + "CHECK CONSTRAINT", SECTION_POST_DATA, + q->data, delq->data, NULL, + coninfo->dobj.dependencies, coninfo->dobj.nDeps, +@@ -11315,7 +11584,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) + coninfo->dobj.name, + tyinfo->dobj.namespace->dobj.name, + NULL, +- tyinfo->rolname, false, ++ tyinfo->rolname, false, false, + "CHECK CONSTRAINT", SECTION_POST_DATA, + q->data, delq->data, NULL, + coninfo->dobj.dependencies, coninfo->dobj.nDeps, +@@ -11601,7 +11870,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) + tbinfo->dobj.namespace->dobj.name, + NULL, + tbinfo->rolname, +- false, "SEQUENCE", SECTION_PRE_DATA, ++ false, false, "SEQUENCE", SECTION_PRE_DATA, + query->data, delqry->data, NULL, + tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, + NULL, NULL); +@@ -11637,7 +11906,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) + tbinfo->dobj.namespace->dobj.name, + NULL, + tbinfo->rolname, +- false, "SEQUENCE OWNED BY", SECTION_PRE_DATA, ++ false, false, "SEQUENCE OWNED BY", SECTION_PRE_DATA, + query->data, "", NULL, + &(tbinfo->dobj.dumpId), 1, + NULL, NULL); +@@ -11650,6 +11919,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) + dumpComment(fout, query->data, + tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, + tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); ++ ++ /* Dump sequence security label */ ++ dumpSecLabel(fout, tbinfo->dobj.dumpId, ++ query->data, ++ tbinfo->dobj.namespace->dobj.name, ++ tbinfo->rellabel); + } + + if (!schemaOnly) +@@ -11665,7 +11940,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) + tbinfo->dobj.namespace->dobj.name, + NULL, + tbinfo->rolname, +- false, "SEQUENCE SET", SECTION_PRE_DATA, ++ false, false, "SEQUENCE SET", SECTION_PRE_DATA, + query->data, "", NULL, + &(tbinfo->dobj.dumpId), 1, + NULL, NULL); +@@ -11849,7 +12124,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) + tginfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + NULL, +- tbinfo->rolname, false, ++ tbinfo->rolname, false, false, + "TRIGGER", SECTION_POST_DATA, + query->data, delqry->data, NULL, + tginfo->dobj.dependencies, tginfo->dobj.nDeps, +@@ -11969,7 +12244,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) + rinfo->dobj.name, + tbinfo->dobj.namespace->dobj.name, + NULL, +- tbinfo->rolname, false, ++ tbinfo->rolname, false, false, + "RULE", SECTION_POST_DATA, + cmd->data, delcmd->data, NULL, + rinfo->dobj.dependencies, rinfo->dobj.nDeps, +@@ -12334,7 +12609,7 @@ fmtQualifiedId(const char *schema, const char *id) + * "", not an invalid "()" column list. + */ + static const char * +-fmtCopyColumnList(const TableInfo *ti) ++fmtCopyColumnList(const TableInfo *ti, bool secids) + { + static PQExpBuffer q = NULL; + int numatts = ti->numatts; +@@ -12350,6 +12625,11 @@ fmtCopyColumnList(const TableInfo *ti) + + appendPQExpBuffer(q, "("); + needComma = false; ++ if (secids) ++ { ++ appendPQExpBuffer(q, "security_label"); ++ needComma = true; ++ } + for (i = 0; i < numatts; i++) + { + if (attisdropped[i]) +diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h +index c93bada..78a50bd 100644 +--- a/src/bin/pg_dump/pg_dump.h ++++ b/src/bin/pg_dump/pg_dump.h +@@ -137,6 +137,7 @@ typedef struct _namespaceInfo + DumpableObject dobj; + char *rolname; /* name of owner, or empty string */ + char *nspacl; ++ char *seclabel; + } NamespaceInfo; + + typedef struct _typeInfo +@@ -153,6 +154,7 @@ typedef struct _typeInfo + char typrelkind; /* 'r', 'v', 'c', etc */ + char typtype; /* 'b', 'c', etc */ + bool isArray; /* true if auto-generated array type */ ++ char *seclabel; + bool isDefined; /* true if typisdefined */ + /* If it's a dumpable base type, we create a "shell type" entry for it */ + struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */ +@@ -227,9 +229,11 @@ typedef struct _tableInfo + bool hasrules; /* does it have any rules? */ + bool hastriggers; /* does it have any triggers? */ + bool hasoids; /* does it have OIDs? */ ++ bool hassecids; /* does it have security-Id? */ + uint32 frozenxid; /* for restore frozen xid */ + int ncheck; /* # of CHECK expressions */ + char *reloftype; /* underlying type for typed table */ ++ char *rellabel; /* relation's security label */ + /* these two are set only if table is a sequence owned by a column: */ + Oid owning_tab; /* OID of table owning sequence */ + int owning_col; /* attr # of column owning sequence */ +@@ -252,6 +256,7 @@ typedef struct _tableInfo + char *attalign; /* attribute align, used by binary_upgrade */ + bool *attislocal; /* true if attr has local definition */ + char **attoptions; /* per-attribute options */ ++ char **attlabels; /* attribute's security label */ + + /* + * Note: we need to store per-attribute notnull, default, and constraint +@@ -287,6 +292,7 @@ typedef struct _tableDataInfo + DumpableObject dobj; + TableInfo *tdtable; /* link to table to dump */ + bool oids; /* include OIDs in data? */ ++ bool secids; /* include SecIDs in data? */ + } TableDataInfo; + + typedef struct _indxInfo +@@ -448,6 +454,7 @@ typedef struct _blobInfo + DumpableObject dobj; + char *rolname; + char *blobacl; ++ char *seclabel; + } BlobInfo; + + /* global decls */ +diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c +index 0c3f63f..83f1678 100644 +--- a/src/bin/pg_dump/pg_dumpall.c ++++ b/src/bin/pg_dump/pg_dumpall.c +@@ -69,6 +69,7 @@ static int disable_triggers = 0; + static int inserts = 0; + static int no_tablespaces = 0; + static int use_setsessauth = 0; ++static int security_label = 0; + static int server_version; + + static FILE *OPF; +@@ -132,6 +133,7 @@ main(int argc, char *argv[]) + {"no-tablespaces", no_argument, &no_tablespaces, 1}, + {"role", required_argument, NULL, 3}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"security-label", no_argument, &security_label, 1}, + + {NULL, 0, NULL, 0} + }; +@@ -285,6 +287,8 @@ main(int argc, char *argv[]) + no_tablespaces = 1; + else if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; ++ else if (strcmp(optarg, "security-label") == 0) ++ security_label = 1; + else + { + fprintf(stderr, +@@ -330,6 +334,8 @@ main(int argc, char *argv[]) + appendPQExpBuffer(pgdumpopts, " --no-tablespaces"); + if (use_setsessauth) + appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); ++ if (security_label) ++ appendPQExpBuffer(pgdumpopts, " --security-label"); + + if (optind < argc) + { +@@ -561,6 +567,7 @@ help(void) + printf(_(" --use-set-session-authorization\n" + " use SET SESSION AUTHORIZATION commands instead of\n" + " ALTER OWNER commands to set ownership\n")); ++ printf(_(" --security-label dump schema/data with security label\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); +@@ -949,6 +956,7 @@ dropTablespaces(PGconn *conn) + static void + dumpTablespaces(PGconn *conn) + { ++ PQExpBuffer qry = createPQExpBuffer(); + PGresult *res; + int i; + +@@ -957,31 +965,37 @@ dumpTablespaces(PGconn *conn) + * pg_xxx) + */ + if (server_version >= 90000) +- res = executeQuery(conn, "SELECT spcname, " ++ appendPQExpBuffer(qry, "SELECT spcname, " + "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " + "spclocation, spcacl, " + "array_to_string(spcoptions, ', ')," + "pg_catalog.shobj_description(oid, 'pg_tablespace') " ++ ",%s AS seclabel " + "FROM pg_catalog.pg_tablespace " + "WHERE spcname !~ '^pg_' " +- "ORDER BY 1"); ++ "ORDER BY 1", ++ security_label ? "security_label" : "NULL"); + else if (server_version >= 80200) +- res = executeQuery(conn, "SELECT spcname, " ++ appendPQExpBuffer(qry, "SELECT spcname, " + "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " + "spclocation, spcacl, null, " + "pg_catalog.shobj_description(oid, 'pg_tablespace') " ++ ",NULL AS seclabel" + "FROM pg_catalog.pg_tablespace " + "WHERE spcname !~ '^pg_' " + "ORDER BY 1"); + else +- res = executeQuery(conn, "SELECT spcname, " ++ appendPQExpBuffer(qry, "SELECT spcname, " + "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " + "spclocation, spcacl, " + "null, null " ++ ",NULL AS seclabel" + "FROM pg_catalog.pg_tablespace " + "WHERE spcname !~ '^pg_' " + "ORDER BY 1"); + ++ res = PQexec(conn, qry->data); ++ + if (PQntuples(res) > 0) + fprintf(OPF, "--\n-- Tablespaces\n--\n\n"); + +@@ -994,6 +1008,7 @@ dumpTablespaces(PGconn *conn) + char *spcacl = PQgetvalue(res, i, 3); + char *spcoptions = PQgetvalue(res, i, 4); + char *spccomment = PQgetvalue(res, i, 5); ++ char *spcseclabel = PQgetvalue(res, i, 6); + char *fspcname; + + /* needed for buildACLCommands() */ +@@ -1010,6 +1025,10 @@ dumpTablespaces(PGconn *conn) + appendPQExpBuffer(buf, "ALTER TABLESPACE %s SET (%s);\n", + fspcname, spcoptions); + ++ if (security_label && strlen(spcseclabel) > 0) ++ appendPQExpBuffer(buf, "ALTER TABLESPACE %s SECURITY LABEL TO '%s';\n", ++ fspcname, spcseclabel); ++ + if (!skip_acls && + !buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner, + "", server_version, buf)) +@@ -1153,48 +1172,48 @@ dumpCreateDB(PGconn *conn) + + /* Now collect all the information about databases to dump */ + if (server_version >= 80400) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "datcollate, datctype, datfrozenxid, " + "datistemplate, datacl, datconnlimit, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " ++ ",%s AS seclabel " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " +- "WHERE datallowconn ORDER BY 1"); ++ "WHERE datallowconn ORDER BY 1", ++ security_label ? "d.security_label" : "NULL"); + else if (server_version >= 80100) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, datconnlimit, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " ++ ",NULL AS seclabel " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 80000) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, -1 as datconnlimit, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " ++ ",NULL AS seclabel " + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70300) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, -1 as datconnlimit, " + "'pg_default' AS dattablespace " ++ ",NULL AS seclabel " + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70100) +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(" + "(select usename from pg_shadow where usesysid=datdba), " + "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " +@@ -1202,6 +1221,7 @@ dumpCreateDB(PGconn *conn) + "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " + "datistemplate, '' as datacl, -1 as datconnlimit, " + "'pg_default' AS dattablespace " ++ ",NULL AS seclabel " + "FROM pg_database d " + "WHERE datallowconn ORDER BY 1"); + else +@@ -1210,18 +1230,20 @@ dumpCreateDB(PGconn *conn) + * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal + * with getting a NULL by not printing any OWNER clause. + */ +- res = executeQuery(conn, +- "SELECT datname, " ++ appendPQExpBuffer(buf, "SELECT datname, " + "(select usename from pg_shadow where usesysid=datdba), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " + "'f' as datistemplate, " + "'' as datacl, -1 as datconnlimit, " + "'pg_default' AS dattablespace " ++ ",NULL AS seclabel " + "FROM pg_database d " + "ORDER BY 1"); + } + ++ res = PQexec(conn, buf->data); ++ + for (i = 0; i < PQntuples(res); i++) + { + char *dbname = PQgetvalue(res, i, 0); +@@ -1234,6 +1256,7 @@ dumpCreateDB(PGconn *conn) + char *dbacl = PQgetvalue(res, i, 7); + char *dbconnlimit = PQgetvalue(res, i, 8); + char *dbtablespace = PQgetvalue(res, i, 9); ++ char *dbseclabel = PQgetvalue(res, i, 10); + char *fdbname; + + fdbname = strdup(fmtId(dbname)); +@@ -1309,6 +1332,9 @@ dumpCreateDB(PGconn *conn) + appendPQExpBuffer(buf, ";\n"); + } + } ++ if (security_label && strlen(dbseclabel) > 0) ++ appendPQExpBuffer(buf, "ALTER DATABASE %s SECURITY LABEL TO '%s';\n", ++ dbname, dbseclabel); + + if (!skip_acls && + !buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner, +diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c +index b0bcbc2..cdb2592 100644 +--- a/src/bin/pg_dump/pg_restore.c ++++ b/src/bin/pg_dump/pg_restore.c +@@ -76,6 +76,7 @@ main(int argc, char **argv) + static int no_data_for_failed_tables = 0; + static int outputNoTablespaces = 0; + static int use_setsessauth = 0; ++ static int no_security_label = 0; + + struct option cmdopts[] = { + {"clean", 0, NULL, 'c'}, +@@ -116,6 +117,7 @@ main(int argc, char **argv) + {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, + {"role", required_argument, NULL, 2}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"no-security-label", no_argument, &no_security_label, 1}, + + {NULL, 0, NULL, 0} + }; +@@ -262,6 +264,8 @@ main(int argc, char **argv) + outputNoTablespaces = 1; + else if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; ++ else if (strcmp(optarg, "no-security-label") == 0) ++ no_security_label = 1; + else + { + fprintf(stderr, +@@ -326,6 +330,7 @@ main(int argc, char **argv) + opts->noDataForFailedTables = no_data_for_failed_tables; + opts->noTablespace = outputNoTablespaces; + opts->use_setsessauth = use_setsessauth; ++ opts->noSecLabel = no_security_label; + + if (opts->formatName) + { +@@ -437,6 +442,7 @@ usage(const char *progname) + " ALTER OWNER commands to set ownership\n")); + printf(_(" -1, --single-transaction\n" + " restore as a single transaction\n")); ++ printf(_(" --no-security-label skip restoration of security labels\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); +diff --git a/src/include/access/htup.h b/src/include/access/htup.h +index d87e888..270ff67 100644 +--- a/src/include/access/htup.h ++++ b/src/include/access/htup.h +@@ -163,7 +163,7 @@ typedef HeapTupleHeaderData *HeapTupleHeader; + #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ + #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ + #define HEAP_HASOID 0x0008 /* has an object-id field */ +-/* bit 0x0010 is available */ ++#define HEAP_HASSECID 0x0010 /* has an security-id field */ + #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ + #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ + #define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */ +@@ -292,6 +292,9 @@ do { \ + (tup)->t_choice.t_datum.datum_typmod = (typmod) \ + ) + ++#define HeapTupleHeaderHasOid(tup) \ ++ ((tup)->t_infomask & HEAP_HASOID) ++ + #define HeapTupleHeaderGetOid(tup) \ + ( \ + ((tup)->t_infomask & HEAP_HASOID) ? \ +@@ -351,6 +354,25 @@ do { \ + (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ + ) + ++#define HeapTupleHeaderHasSecid(tup) \ ++ ((tup)->t_infomask & HEAP_HASSECID) ++ ++#define HeapTupleHeaderGetSecid(tup) \ ++ ( \ ++ HeapTupleHeaderHasSecid(tup) \ ++ ? (*(Oid *)((char *)(tup) + (tup)->t_hoff \ ++ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ ++ - sizeof(Oid))) \ ++ : InvalidOid \ ++ ) ++ ++#define HeapTupleHeaderSetSecid(tup, secid) \ ++ do { \ ++ Assert(HeapTupleHeaderHasSecid(tup)); \ ++ *((Oid *)((char *)(tup) + (tup)->t_hoff \ ++ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ ++ - sizeof(Oid))) = (secid); \ ++ } while(0) + + /* + * BITMAPLEN(NATTS) - +@@ -545,12 +567,23 @@ typedef HeapTupleData *HeapTuple; + #define HeapTupleClearHeapOnly(tuple) \ + HeapTupleHeaderClearHeapOnly((tuple)->t_data) + ++#define HeapTupleHasOid(tuple) \ ++ HeapTupleHeaderHasOid((tuple)->t_data) ++ + #define HeapTupleGetOid(tuple) \ + HeapTupleHeaderGetOid((tuple)->t_data) + + #define HeapTupleSetOid(tuple, oid) \ + HeapTupleHeaderSetOid((tuple)->t_data, (oid)) + ++#define HeapTupleHasSecid(tuple) \ ++ HeapTupleHeaderHasSecid((tuple)->t_data) ++ ++#define HeapTupleGetSecid(tuple) \ ++ HeapTupleHeaderGetSecid((tuple)->t_data) ++ ++#define HeapTupleSetSecid(tuple, secid) \ ++ HeapTupleHeaderSetSecid((tuple)->t_data, (secid)) + + /* + * WAL record definitions for heapam.c's WAL operations +diff --git a/src/include/access/sysattr.h b/src/include/access/sysattr.h +index 59cd2cd..ad5903b 100644 +--- a/src/include/access/sysattr.h ++++ b/src/include/access/sysattr.h +@@ -25,7 +25,8 @@ + #define MaxTransactionIdAttributeNumber (-5) + #define MaxCommandIdAttributeNumber (-6) + #define TableOidAttributeNumber (-7) +-#define FirstLowInvalidHeapAttributeNumber (-8) ++#define SecurityLabelAttributeNumber (-8) ++#define FirstLowInvalidHeapAttributeNumber (-9) + + + #endif /* SYSATTR_H */ +diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h +index 53e0be6..dea713b 100644 +--- a/src/include/access/tupdesc.h ++++ b/src/include/access/tupdesc.h +@@ -75,13 +75,14 @@ typedef struct tupleDesc + Oid tdtypeid; /* composite type ID for tuple type */ + int32 tdtypmod; /* typmod for tuple type */ + bool tdhasoid; /* tuple has oid attribute in its header */ ++ bool tdhassecid; /* tuple has security id in its header */ + int tdrefcount; /* reference count, or -1 if not counting */ + } *TupleDesc; + + +-extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid); ++extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid, bool hassecid); + +-extern TupleDesc CreateTupleDesc(int natts, bool hasoid, ++extern TupleDesc CreateTupleDesc(int natts, bool hasoid, bool hassecid, + Form_pg_attribute *attrs); + + extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc); +diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h +index 49a32e3..f088876 100644 +--- a/src/include/bootstrap/bootstrap.h ++++ b/src/include/bootstrap/bootstrap.h +@@ -24,6 +24,7 @@ typedef enum + BgWriterProcess, + WalWriterProcess, + WalReceiverProcess, ++ SecurityWorkerProcess, + + NUM_AUXPROCTYPES /* Must be last! */ + } AuxProcType; +diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h +index 8292273..e2a86e8 100644 +--- a/src/include/catalog/heap.h ++++ b/src/include/catalog/heap.h +@@ -61,7 +61,8 @@ extern Oid heap_create_with_catalog(const char *relname, + OnCommitAction oncommit, + Datum reloptions, + bool use_user_acl, +- bool allow_system_table_mods); ++ bool allow_system_table_mods, ++ Oid *secLabels); + + extern void heap_drop_with_catalog(Oid relid); + +@@ -75,13 +76,15 @@ extern List *heap_truncate_find_FKs(List *relationIds); + + extern void InsertPgAttributeTuple(Relation pg_attribute_rel, + Form_pg_attribute new_attribute, +- CatalogIndexState indstate); ++ CatalogIndexState indstate, ++ Oid securityId); + + extern void InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, + Datum relacl, +- Datum reloptions); ++ Datum reloptions, ++ Oid securityId); + + extern List *AddRelationNewConstraints(Relation rel, + List *newColDefaults, +@@ -106,10 +109,13 @@ extern void RemoveAttrDefaultById(Oid attrdefId); + extern void RemoveStatistics(Oid relid, AttrNumber attnum); + + extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno, +- bool relhasoids); ++ bool relhasoids, bool relhassecids); + + extern Form_pg_attribute SystemAttributeByName(const char *attname, +- bool relhasoids); ++ bool relhasoids, bool relhassecids); ++ ++extern bool SystemAttributeWritable(AttrNumber attno, ++ bool relhasoids, bool relhassecids); + + extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind, + bool allow_system_table_mods); +diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h +index 4f437fd..069256f 100644 +--- a/src/include/catalog/indexing.h ++++ b/src/include/catalog/indexing.h +@@ -255,6 +255,11 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index, 2703, on pg_type using btree(oid oid_ops + DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); + #define TypeNameNspIndexId 2704 + ++DECLARE_UNIQUE_INDEX(pg_seclabel_secid_index, 3401, on pg_seclabel using btree(secid oid_ops, datid oid_ops, relid oid_ops)); ++#define SecLabelSecidIndexId 3401 ++DECLARE_INDEX(pg_seclabel_label_index, 3402, on pg_seclabel using btree(datid oid_ops, relid oid_ops, label text_ops)); ++#define SecLabelLabelIndexId 3402 ++ + DECLARE_UNIQUE_INDEX(pg_foreign_data_wrapper_oid_index, 112, on pg_foreign_data_wrapper using btree(oid oid_ops)); + #define ForeignDataWrapperOidIndexId 112 + +diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h +index 5ea514d..0f07482 100644 +--- a/src/include/catalog/pg_class.h ++++ b/src/include/catalog/pg_class.h +@@ -60,6 +60,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO + */ + int2 relchecks; /* # of CHECK constraints for class */ + bool relhasoids; /* T if we generate OIDs for rows of rel */ ++ bool relhassecids; /* T if we generate SIDs for rows of rel */ + bool relhaspkey; /* has (or has had) PRIMARY KEY index */ + bool relhasexclusion; /* has (or has had) exclusion constraint */ + bool relhasrules; /* has (or has had) any rules */ +@@ -93,7 +94,7 @@ typedef FormData_pg_class *Form_pg_class; + * ---------------- + */ + +-#define Natts_pg_class 27 ++#define Natts_pg_class 28 + #define Anum_pg_class_relname 1 + #define Anum_pg_class_relnamespace 2 + #define Anum_pg_class_reltype 3 +@@ -113,14 +114,15 @@ typedef FormData_pg_class *Form_pg_class; + #define Anum_pg_class_relnatts 17 + #define Anum_pg_class_relchecks 18 + #define Anum_pg_class_relhasoids 19 +-#define Anum_pg_class_relhaspkey 20 +-#define Anum_pg_class_relhasexclusion 21 +-#define Anum_pg_class_relhasrules 22 +-#define Anum_pg_class_relhastriggers 23 +-#define Anum_pg_class_relhassubclass 24 +-#define Anum_pg_class_relfrozenxid 25 +-#define Anum_pg_class_relacl 26 +-#define Anum_pg_class_reloptions 27 ++#define Anum_pg_class_relhassecids 20 ++#define Anum_pg_class_relhaspkey 21 ++#define Anum_pg_class_relhasexclusion 22 ++#define Anum_pg_class_relhasrules 23 ++#define Anum_pg_class_relhastriggers 24 ++#define Anum_pg_class_relhassubclass 25 ++#define Anum_pg_class_relfrozenxid 26 ++#define Anum_pg_class_relacl 27 ++#define Anum_pg_class_reloptions 28 + + /* ---------------- + * initial contents of pg_class +@@ -132,13 +134,13 @@ typedef FormData_pg_class *Form_pg_class; + */ + + /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */ +-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ )); ++DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t t f f f f f 3 _null_ _null_ )); + DESCR(""); +-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ )); ++DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f t f f f f f 3 _null_ _null_ )); + DESCR(""); +-DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f f r 25 0 t f f f f f 3 _null_ _null_ )); ++DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f f r 25 0 t t f f f f f 3 _null_ _null_ )); + DESCR(""); +-DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f f r 27 0 t f f f f f 3 _null_ _null_ )); ++DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t t f f f f f 3 _null_ _null_ )); + DESCR(""); + + #define RELKIND_INDEX 'i' /* secondary index */ +diff --git a/src/include/catalog/pg_conversion_fn.h b/src/include/catalog/pg_conversion_fn.h +index d40dea6..add5fd9 100644 +--- a/src/include/catalog/pg_conversion_fn.h ++++ b/src/include/catalog/pg_conversion_fn.h +@@ -17,7 +17,7 @@ + extern Oid ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +- Oid conproc, bool def); ++ Oid conproc, bool def, Oid securityId); + extern void RemoveConversionById(Oid conversionOid); + extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); + +diff --git a/src/include/catalog/pg_largeobject.h b/src/include/catalog/pg_largeobject.h +index c4c4a26..6025f56 100644 +--- a/src/include/catalog/pg_largeobject.h ++++ b/src/include/catalog/pg_largeobject.h +@@ -51,9 +51,10 @@ typedef FormData_pg_largeobject *Form_pg_largeobject; + #define Anum_pg_largeobject_pageno 2 + #define Anum_pg_largeobject_data 3 + +-extern Oid LargeObjectCreate(Oid loid); ++extern Oid LargeObjectCreate(Oid loid, Oid securityId); + extern void LargeObjectDrop(Oid loid); + extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId); ++extern void LargeObjectAlterSecLabel(Oid loid, char *new_label); + extern bool LargeObjectExists(Oid loid); + + #endif /* PG_LARGEOBJECT_H */ +diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h +index 425f039..0f8043d 100644 +--- a/src/include/catalog/pg_namespace.h ++++ b/src/include/catalog/pg_namespace.h +@@ -77,6 +77,6 @@ DESCR("standard public schema"); + /* + * prototypes for functions in pg_namespace.c + */ +-extern Oid NamespaceCreate(const char *nspName, Oid ownerId); ++extern Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid secid); + + #endif /* PG_NAMESPACE_H */ +diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h +index f2751a4..3e024b3 100644 +--- a/src/include/catalog/pg_proc.h ++++ b/src/include/catalog/pg_proc.h +@@ -3719,6 +3719,10 @@ DESCR("current user privilege on role by role name"); + DATA(insert OID = 2710 ( pg_has_role PGNSP PGUID 12 1 0 0 f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ pg_has_role_id _null_ _null_ _null_ )); + DESCR("current user privilege on role by role oid"); + ++/* SE-PostgreSQL related stuff */ ++DATA(insert OID = 3405 ( 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 = 3407 ( 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 = 1269 ( pg_column_size PGNSP PGUID 12 1 0 0 f f f t f s 1 0 23 "2276" _null_ _null_ _null_ _null_ pg_column_size _null_ _null_ _null_ )); + DESCR("bytes required to store the value, perhaps with compression"); + DATA(insert OID = 2322 ( pg_tablespace_size PGNSP PGUID 12 1 0 0 f f f t f v 1 0 20 "26" _null_ _null_ _null_ _null_ pg_tablespace_size_oid _null_ _null_ _null_ )); +diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h +index c886f81..f2351c1 100644 +--- a/src/include/catalog/pg_proc_fn.h ++++ b/src/include/catalog/pg_proc_fn.h +@@ -37,7 +37,8 @@ extern Oid ProcedureCreate(const char *procedureName, + List *parameterDefaults, + Datum proconfig, + float4 procost, +- float4 prorows); ++ float4 prorows, ++ Oid prosecid); + + extern bool function_parse_error_transpose(const char *prosrc); + +diff --git a/src/include/catalog/pg_seclabel.h b/src/include/catalog/pg_seclabel.h +new file mode 100644 +index 0000000..21b25de +--- /dev/null ++++ b/src/include/catalog/pg_seclabel.h +@@ -0,0 +1,79 @@ ++/* ++ * pg_seclabel.h ++ * Definition of the security label relation (pg_seclabel) ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++#ifndef PG_SECLABEL_H ++#define PG_SECLABEL_H ++ ++#include "catalog/genbki.h" ++ ++#include "access/htup.h" ++#include "access/skey.h" ++#include "utils/relcache.h" ++ ++#define SecLabelRelationId 3400 ++ ++CATALOG(pg_seclabel,3400) BKI_SHARED_RELATION BKI_WITHOUT_OIDS ++{ ++ /* Identifier of the security label */ ++ Oid secid; ++ ++ /* OID of the database which referes the entry */ ++ Oid datid; ++ ++ /* OID of the table which refers the entry */ ++ Oid relid; ++ ++ /* Text representation of the security label */ ++ text label; ++} FormData_pg_seclabel; ++ ++/* ++ * Form_pg_seclabel corresponds to a pointer to a tuple with ++ * the format of pg_seclabel relation. ++ */ ++typedef FormData_pg_seclabel *Form_pg_seclabel; ++ ++/* ++ * Compiler constants for pg_seclabel ++ */ ++#define Natts_pg_seclabel 4 ++#define Anum_pg_seclabel_secid 1 ++#define Anum_pg_seclabel_datid 2 ++#define Anum_pg_seclabel_relid 3 ++#define Anum_pg_seclabel_label 4 ++ ++/* ++ * Functions to translate between security label and identifier ++ */ ++extern bool ignore_security_label_input; ++ ++extern bool seclabelCatalogHasSysAttr(Oid relOid); ++extern void seclabelPostBootstrap(void); ++extern void seclabelOnCreateDatabase(Oid src_datOid, Oid dst_datOid); ++extern void seclabelOnDropDatabase(Oid datOid); ++extern void seclabelOnDropTable(Oid relOid); ++ ++extern Oid *seclabelMakeRelationDefaults(TupleDesc tupdesc, List *supOids); ++extern Oid *seclabelMakeToastDefaults(TupleDesc tupdesc, Oid relOid); ++ ++extern Oid seclabelGetNewSecid(Relation rel, HeapTuple tuple); ++ ++extern Oid seclabelRawInput(Oid relOid, char *seclabel); ++extern char *seclabelRawOutput(Oid relOid, Oid secid); ++extern Oid seclabelTransInput(Oid relOid, char *seclabel); ++extern char *seclabelTransOutput(Oid relOid, Oid secid); ++ ++extern Oid seclabelMoveSecid(Oid dst_relid, Oid src_relid, Oid secid); ++extern bool seclabelCompareSecid(Oid relid1, Oid secid1, ++ Oid relid2, Oid secid2); ++ ++extern Datum seclabelSysattOutput(Oid relOid, HeapTuple tuple); ++ ++extern void seclabelRelationReclaim(Oid relOid); ++extern Datum seclabel_to_secid(PG_FUNCTION_ARGS); ++ ++#endif /* PG_SECLABEL_H */ +diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h +index 5cf90af..1ca2dd4 100644 +--- a/src/include/catalog/pg_type_fn.h ++++ b/src/include/catalog/pg_type_fn.h +@@ -50,7 +50,8 @@ extern Oid TypeCreate(Oid newTypeOid, + char storage, + int32 typeMod, + int32 typNDims, +- bool typeNotNull); ++ bool typeNotNull, ++ Oid securityId); + + extern void GenerateTypeDependencies(Oid typeNamespace, + Oid typeObjectId, +diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h +index fb6f678..f642ded 100644 +--- a/src/include/catalog/toasting.h ++++ b/src/include/catalog/toasting.h +@@ -58,5 +58,8 @@ DECLARE_TOAST(pg_shdescription, 2846, 2847); + DECLARE_TOAST(pg_db_role_setting, 2966, 2967); + #define PgDbRoleSettingToastTable 2966 + #define PgDbRoleSettingToastIndex 2967 ++DECLARE_TOAST(pg_seclabel, 3403, 3404); ++#define PgSecLabelToastTable 3403 ++#define PgSecLabelToastIndex 3404 + + #endif /* TOASTING_H */ +diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h +index b1e04fb..6cd19ba 100644 +--- a/src/include/commands/alter.h ++++ b/src/include/commands/alter.h +@@ -19,5 +19,6 @@ + extern void ExecRenameStmt(RenameStmt *stmt); + extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); + extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); ++extern void ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt); + + #endif /* ALTER_H */ +diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h +index 542fc27..874e07a 100644 +--- a/src/include/commands/dbcommands.h ++++ b/src/include/commands/dbcommands.h +@@ -58,6 +58,7 @@ extern void RenameDatabase(const char *oldname, const char *newname); + extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel); + extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt); + extern void AlterDatabaseOwner(const char *dbname, Oid newOwnerId); ++extern void AlterDatabaseSecLabel(const char *dbname, char *new_label); + + extern Oid get_database_oid(const char *dbname); + extern char *get_database_name(Oid dbid); +diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h +index e8dbe81..7fc4922 100644 +--- a/src/include/commands/defrem.h ++++ b/src/include/commands/defrem.h +@@ -60,6 +60,8 @@ extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType); + extern void RenameFunction(List *name, List *argtypes, const char *newname); + extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId); + extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId); ++extern void AlterFunctionSecLabel(List *name, List *argtypes, ++ bool isagg, char *new_label); + extern void AlterFunction(AlterFunctionStmt *stmt); + extern void CreateCast(CreateCastStmt *stmt); + extern void DropCast(DropCastStmt *stmt); +diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h +index c914bd5..edd5abd 100644 +--- a/src/include/commands/schemacmds.h ++++ b/src/include/commands/schemacmds.h +@@ -26,5 +26,6 @@ extern void RemoveSchemaById(Oid schemaOid); + extern void RenameSchema(const char *oldname, const char *newname); + extern void AlterSchemaOwner(const char *name, Oid newOwnerId); + extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId); ++extern void AlterSchemaSecLabel(const char *name, char *new_label); + + #endif /* SCHEMACMDS_H */ +diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h +index f9269cc..a531f45 100644 +--- a/src/include/commands/tablecmds.h ++++ b/src/include/commands/tablecmds.h +@@ -35,6 +35,13 @@ extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, + Oid oldNspOid, Oid newNspOid, + bool hasDependEntry); + ++extern void AlterRelationSecLabel(RangeVar *relation, const char *attname, ++ ObjectType objtype, char *new_label); ++extern void AlterRelationSecLabelInternal(Oid relOid, Oid securityId, ++ int expected_parents); ++extern void AlterAttributeSecLabelInternal(Oid relOid, const char *attname, ++ Oid securityId, int expected_parents); ++ + extern void CheckTableNotInUse(Relation rel, const char *stmt); + + extern void ExecuteTruncate(TruncateStmt *stmt); +diff --git a/src/include/commands/tablespace.h b/src/include/commands/tablespace.h +index cf005ee..e9e8a55 100644 +--- a/src/include/commands/tablespace.h ++++ b/src/include/commands/tablespace.h +@@ -44,6 +44,7 @@ extern void DropTableSpace(DropTableSpaceStmt *stmt); + extern void RenameTableSpace(const char *oldname, const char *newname); + extern void AlterTableSpaceOwner(const char *name, Oid newOwnerId); + extern void AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt); ++extern void AlterTableSpaceSecLabel(const char *tspaceName, char *newLabel); + + extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo); + +diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h +index 8e5b610..f8c5872 100644 +--- a/src/include/commands/typecmds.h ++++ b/src/include/commands/typecmds.h +@@ -43,5 +43,7 @@ extern void AlterTypeNamespace(List *names, const char *newschema); + extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, + bool isImplicitArray, + bool errorOnTableType); ++extern void AlterTypeSecLabel(List *name, char *new_label); ++extern void AlterTypeSecLabelInternal(Oid typeOid, Oid securityId); + + #endif /* TYPECMDS_H */ +diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h +index 820314c..7873b44 100644 +--- a/src/include/executor/executor.h ++++ b/src/include/executor/executor.h +@@ -131,8 +131,8 @@ extern TupleHashEntry FindTupleHashEntry(TupleHashTable hashtable, + /* + * prototypes from functions in execJunk.c + */ +-extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, +- TupleTableSlot *slot); ++extern JunkFilter *ExecInitJunkFilter(List *targetList, ++ bool hasoid, bool hassecid, TupleTableSlot *slot); + extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, + TupleDesc cleanTupType, + TupleTableSlot *slot); +@@ -164,6 +164,7 @@ extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, + int instrument_options); + extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); + extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); ++extern bool ExecContextForcesSecids(PlanState *planstate, bool *hassecid); + extern void ExecConstraints(ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, EState *estate); + extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate, +@@ -234,8 +235,8 @@ extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate); + extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); + extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, + TupleDesc tupType); +-extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); +-extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); ++extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid); ++extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid); + extern TupleDesc ExecTypeFromExprList(List *exprList); + extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); + +diff --git a/src/include/fmgr.h b/src/include/fmgr.h +index d1a0dc1..e9de809 100644 +--- a/src/include/fmgr.h ++++ b/src/include/fmgr.h +@@ -51,6 +51,7 @@ typedef struct FmgrInfo + bool fn_retset; /* function returns a set */ + unsigned char fn_stats; /* collect stats if track_functions > this */ + void *fn_extra; /* extra space for use by handler */ ++ char *fn_seclabel; /* function is trusted procedure, or NULL */ + MemoryContext fn_mcxt; /* memory context to store fn_extra in */ + fmNodePtr fn_expr; /* expression parse tree for call, or NULL */ + } FmgrInfo; +diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h +index 1b5e476..37ffdfa 100644 +--- a/src/include/nodes/nodes.h ++++ b/src/include/nodes/nodes.h +@@ -346,6 +346,7 @@ typedef enum NodeTag + T_AlterUserMappingStmt, + T_DropUserMappingStmt, + T_AlterTableSpaceOptionsStmt, ++ T_AlterSecLabelStmt, + + /* + * TAGS FOR PARSE TREE NODES (parsenodes.h) +diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h +index b591073..53609e8 100644 +--- a/src/include/nodes/parsenodes.h ++++ b/src/include/nodes/parsenodes.h +@@ -731,6 +731,7 @@ typedef struct RangeTblEntry + Oid checkAsUser; /* if valid, check access as this role */ + Bitmapset *selectedCols; /* columns needing SELECT permission */ + Bitmapset *modifiedCols; /* columns needing INSERT/UPDATE permission */ ++ uint32 rowlvPerms; /* permissions for row-level access controls */ + } RangeTblEntry; + + /* +@@ -1134,6 +1135,8 @@ typedef enum AlterTableType + AT_DropCluster, /* SET WITHOUT CLUSTER */ + AT_AddOids, /* SET WITH OIDS */ + AT_DropOids, /* SET WITHOUT OIDS */ ++ AT_AddSecLabel, /* SET WITH SECURITY LABEL */ ++ AT_DropSecLabel, /* SET WITHOUT SECURITY LABEL */ + AT_SetTableSpace, /* SET TABLESPACE */ + AT_SetRelOptions, /* SET (...) -- AM specific parameters */ + AT_ResetRelOptions, /* RESET (...) -- AM specific parameters */ +@@ -2073,6 +2076,20 @@ typedef struct AlterOwnerStmt + char *newowner; /* the new owner */ + } AlterOwnerStmt; + ++/* ---------------------- ++ * Alter Security Label Statement ++ * ---------------------- ++ */ ++typedef struct AlterSecLabelStmt ++{ ++ NodeTag type; ++ ObjectType objectType; /* OBJECT_TABLE, OBJECT_TYPE, etc */ ++ RangeVar *relation; /* in case it's a table */ ++ List *object; /* in case it's some other object */ ++ List *objarg; /* argument types, if applicable */ ++ char *addname; /* additional name if needed */ ++ Value *secLabel; /* the new security label */ ++} AlterSecLabelStmt; + + /* ---------------------- + * Create Rule Statement +diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h +index 49d4b6c..13c88a1 100644 +--- a/src/include/parser/kwlist.h ++++ b/src/include/parser/kwlist.h +@@ -208,6 +208,7 @@ PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD) + PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD) + PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD) + PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD) ++PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD) + PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD) + PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD) + PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD) +diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in +index 684aed4..2080c95 100644 +--- a/src/include/pg_config.h.in ++++ b/src/include/pg_config.h.in +@@ -412,6 +412,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_SECURITY_PAM_APPL_H + ++/* Define to 1 if you enable SELinux support */ ++#undef HAVE_SELINUX ++ + /* Define to 1 if you have the `setproctitle' function. */ + #undef HAVE_SETPROCTITLE + +diff --git a/src/include/sepgsql/hooks.h b/src/include/sepgsql/hooks.h +new file mode 100644 +index 0000000..b929e6d +--- /dev/null ++++ b/src/include/sepgsql/hooks.h +@@ -0,0 +1,293 @@ ++/* ++ * sepgsql/hooks.h ++ * ++ * Header of SE-PostgreSQL Hooks ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#ifndef SEPGSQL_HOOKS_H ++#define SEPGSQL_HOOKS_H ++ ++#include "executor/tuptable.h" ++#include "nodes/plannodes.h" ++#include "nodes/relation.h" ++#include "storage/fd.h" ++#include "utils/acl.h" ++#include "utils/rel.h" ++#include "utils/snapshot.h" ++ ++/* ++ * misc.c ++ */ ++extern char *sepgsql_get_client_label(void); ++extern char *sepgsql_set_client_label(char *new_label); ++extern void sepgsql_post_bootstraping(void); ++extern void sepgsql_initialize(void); ++extern bool sepgsql_worker_needed(void); ++extern void sepgsql_worker_main(void); ++ ++extern Datum sepgsql_getcon(PG_FUNCTION_ARGS); ++ ++/* ++ * database.c ++ */ ++extern Oid sepgsql_database_create(const char *datName, Oid templateOid); ++extern void sepgsql_database_alter(Oid databaseOid); ++extern Oid sepgsql_database_relabel(Oid databaseOid, char *new_label); ++extern void sepgsql_database_drop(Oid databaseOid, bool cascade); ++extern void sepgsql_database_grant(Oid databaseOid); ++extern void sepgsql_database_comment(Oid databaseOid); ++extern void sepgsql_database_connect(Oid databaseOid); ++extern void sepgsql_database_reindex(Oid databaseOid); ++extern void sepgsql_database_getattr(Oid databaseOid); ++ ++/* ++ * schema.c ++ */ ++extern Oid sepgsql_schema_create(const char *nspName, bool is_temp); ++extern void sepgsql_schema_alter(Oid namespaceOid); ++extern Oid sepgsql_schema_relabel(Oid namespaceOid, char *new_label); ++extern void sepgsql_schema_drop(Oid namespaceOid, bool cascade); ++extern void sepgsql_schema_grant(Oid namespaceOid); ++extern bool sepgsql_schema_search(Oid namespaceOid, bool abort); ++extern void sepgsql_schema_comment(Oid namespaceOid); ++ ++/* ++ * relation.c ++ */ ++extern bool sepgsql_relation_perms(Oid relOid, AclMode aclmask, ++ Bitmapset *selectedCols, ++ Bitmapset *modifiedCols, bool abort); ++extern Oid *sepgsql_relation_create(const char *relName, ++ char relkind, ++ TupleDesc tupDesc, ++ Oid namespaceId, ++ List *supOids, ++ bool createAs); ++extern void sepgsql_relation_alter(Oid relationOid); ++extern void sepgsql_relation_alter_schema(Oid relationOid, Oid newSchema); ++extern void sepgsql_relation_alter_rename(Oid relationOid, ++ const char *newName); ++extern void sepgsql_relation_alter_inherit(Oid childOid, Oid parentOid); ++extern Oid sepgsql_relation_relabel(Oid relationOid, char *new_label); ++extern void sepgsql_relation_drop(Oid relationOid, bool cascade); ++extern void sepgsql_relation_getattr(Oid relationOid); ++extern void sepgsql_relation_grant(Oid relationOid); ++extern void sepgsql_relation_comment(Oid relationOid); ++extern bool sepgsql_relation_cluster(Oid relationOid, bool abort); ++extern void sepgsql_relation_truncate(Relation rel); ++extern void sepgsql_relation_lock(Relation rel); ++extern void sepgsql_relation_reindex(Oid relOid); ++extern void sepgsql_view_replace(Oid viewOid); ++extern void sepgsql_index_create(Oid relationOid, Oid namespaceOid); ++extern void sepgsql_index_reindex(Oid indexOid); ++extern void sepgsql_sequence_get_value(Oid sequenceOid); ++extern void sepgsql_sequence_next_value(Oid sequenceOid); ++extern void sepgsql_sequence_set_value(Oid sequenceOid); ++extern void sepgsql_rule_create(Oid relationOid, ++ const char *ruleName); ++extern void sepgsql_rule_drop(Oid relationOid, ++ const char *ruleName, bool cascade); ++extern void sepgsql_rule_comment(Oid relationOid, const char *ruleName); ++extern void sepgsql_trigger_create(Oid relationOid, const char *triggerName, ++ Oid constrrelid, Oid funcOid); ++extern void sepgsql_trigger_alter(Oid relOid, const char *tgName); ++extern void sepgsql_trigger_drop(Oid relOid, const char *tgName, bool cascade); ++extern void sepgsql_trigger_comment(Oid relOid, const char *tgName); ++extern void sepgsql_constraint_comment(Oid relOid, const char *constName); ++ ++/* ++ * attribute.c ++ */ ++extern Oid sepgsql_attribute_create(Oid relOid, const char *attName); ++extern void sepgsql_attribute_alter(Oid relOid, const char *attName); ++extern Oid sepgsql_attribute_relabel(Oid relOid, const char *attName, ++ char *new_label); ++extern void sepgsql_attribute_drop(Oid relOid, const char *attName, bool cascade); ++extern void sepgsql_attribute_grant(Oid relOid, AttrNumber attnum); ++extern void sepgsql_attribute_comment(Oid relOid, AttrNumber attnum); ++ ++/* ++ * proc.c ++ */ ++extern Oid sepgsql_proc_create(const char *proName, Oid replaced, ++ Oid namespaceOid, Oid langageOid); ++extern void sepgsql_proc_alter(Oid procOid); ++extern void sepgsql_proc_alter_rename(Oid procOid, const char *newName); ++extern void sepgsql_proc_alter_schema(Oid procOid, Oid newSchema); ++extern Oid sepgsql_proc_relabel(Oid procOid, char *new_label); ++extern void sepgsql_proc_drop(Oid procOid, bool cascade); ++extern void sepgsql_proc_grant(Oid procOid); ++extern void sepgsql_proc_comment(Oid procOid); ++extern void sepgsql_proc_execute(Oid procOid); ++extern bool sepgsql_proc_be_inlined(HeapTuple protup); ++extern char *sepgsql_proc_domtrans(HeapTuple protup, MemoryContext mcxt); ++extern Oid sepgsql_aggregate_create(const char *aggName, Oid namespaceId, ++ Oid transFunc, Oid finalFunc); ++extern void sepgsql_aggregate_execute(Oid aggOid); ++ ++/* ++ * type.c ++ */ ++extern Oid sepgsql_type_create(const char *typeName, Oid replaced, ++ Oid namespaceId, char typeType, ++ Oid inputFunc, Oid outputFunc, ++ Oid recvFunc, Oid sendFunc, ++ Oid modinFunc, Oid modoutFunc, ++ Oid analyzeFunc); ++extern void sepgsql_type_alter(Oid typeOid); ++extern void sepgsql_type_alter_rename(Oid typeOid, const char *newName); ++extern void sepgsql_type_alter_schema(Oid typeOid, Oid newSchema); ++extern Oid sepgsql_type_relabel(Oid typeOid, char *newLabel); ++extern void sepgsql_type_drop(Oid typeOid, bool cascade); ++extern void sepgsql_type_comment(Oid typeOid); ++extern Oid sepgsql_cast_create(Oid sourceTypeOid, Oid targetTypeOid, ++ char castMethod, Oid castFuncOid); ++extern void sepgsql_cast_drop(Oid srcTypeOid, Oid dstTypeOid, bool cascade); ++extern void sepgsql_cast_comment(Oid srcTypeOid, Oid dstTypeOid); ++ ++/* ++ * tablespace.h ++ */ ++extern Oid sepgsql_tablespace_create(const char *tablespaceName); ++extern void sepgsql_tablespace_alter(Oid tablespaceOid); ++extern Oid sepgsql_tablespace_relabel(Oid tablespaceOid, char *newLabel); ++extern void sepgsql_tablespace_drop(Oid tablespaceOid, bool cascade); ++extern void sepgsql_tablespace_grant(Oid tablespaceOid); ++extern void sepgsql_tablespace_getattr(Oid tablespaceOid); ++extern void sepgsql_tablespace_comment(Oid tablespaceOid); ++ ++/* ++ * operator.h ++ */ ++extern Oid sepgsql_operator_create(const char *operName, Oid replaced, ++ Oid namespaceId, ++ Oid codeFn, Oid restrictFn, Oid joinFn, ++ Oid commutatorOp, Oid negatorOp); ++extern void sepgsql_operator_alter(Oid operOid); ++extern Oid sepgsql_operator_relabel(Oid operOid, char *newLabel); ++extern void sepgsql_operator_drop(Oid operOid, bool cascade); ++extern void sepgsql_operator_comment(Oid operOid); ++ ++extern Oid sepgsql_opclass_create(const char *opcName, Oid namespaceId, ++ Oid typeOid, Oid opfamilyOid, Oid storageOid); ++extern void sepgsql_opclass_alter(Oid opcOid); ++extern void sepgsql_opclass_alter_rename(Oid opcOid, const char *newName); ++extern void sepgsql_opclass_drop(Oid opcOid, bool cascade); ++extern void sepgsql_opclass_comment(Oid opcOid); ++ ++extern Oid sepgsql_opfamily_create(const char *opfName, ++ Oid namespaceId, Oid amOid); ++extern void sepgsql_opfamily_alter(Oid opfOid, bool isDrop, Oid amOid, ++ List *operators, List *procedures); ++extern void sepgsql_opfamily_alter_rename(Oid opfOid, const char *newName); ++extern void sepgsql_opfamily_alter_owner(Oid opfOid, Oid newOwner); ++extern void sepgsql_opfamily_drop(Oid opfOid, bool cascade); ++extern void sepgsql_opfamily_comment(Oid opfOid); ++ ++/* ++ * role.c ++ */ ++extern Oid sepgsql_role_create(const char *roleName); ++extern void sepgsql_role_alter(Oid roleOid); ++extern Oid sepgsql_role_relabel(Oid roleOid, char *newLabel); ++extern void sepgsql_role_drop(Oid roleOid, bool cascade); ++extern void sepgsql_role_grant(Oid roleOid, bool is_grant, List *memberIds); ++extern void sepgsql_role_comment(Oid roleOid); ++ ++/* ++ * blob.c ++ */ ++extern Oid sepgsql_largeobject_create(Oid loid); ++extern void sepgsql_largeobject_alter(Oid loid); ++extern Oid sepgsql_largeobject_relabel(Oid loid, char *newLabel); ++extern void sepgsql_largeobject_drop(Oid loid, bool cascade); ++extern void sepgsql_largeobject_read(Oid loid, Snapshot snapshot); ++extern void sepgsql_largeobject_write(Oid loid, Snapshot snapshot); ++extern Oid sepgsql_largeobject_import(Oid loid, const char *filename); ++extern void sepgsql_largeobject_export(Oid loid, Snapshot snapshot, ++ const char *filename); ++extern void sepgsql_largeobject_grant(Oid loid); ++extern void sepgsql_largeobject_comment(Oid loid); ++ ++/* ++ * conversion.c ++ */ ++extern Oid sepgsql_conversion_create(const char *convName, ++ Oid namespaceId, Oid conversionFunc); ++extern void sepgsql_conversion_alter(Oid convOid); ++extern void sepgsql_conversion_alter_rename(Oid convOid, const char *newName); ++extern void sepgsql_conversion_drop(Oid convOid, bool cascade); ++extern void sepgsql_conversion_comment(Oid convOid); ++ ++/* ++ * tsearch.c ++ */ ++extern Oid sepgsql_ts_config_create(const char *confName, Oid namespaceId); ++extern void sepgsql_ts_config_alter(Oid confOid); ++extern void sepgsql_ts_config_alter_rename(Oid confOid, const char *newName); ++extern void sepgsql_ts_config_drop(Oid confOid, bool cascade); ++extern void sepgsql_ts_config_comment(Oid confOid); ++extern Oid sepgsql_ts_dict_create(const char *dictName, Oid namespaceId); ++extern void sepgsql_ts_dict_alter(Oid dictOid); ++extern void sepgsql_ts_dict_alter_rename(Oid dictOid, const char *newName); ++extern void sepgsql_ts_dict_drop(Oid dictOid, bool cascade); ++extern void sepgsql_ts_dict_comment(Oid dictOid); ++extern Oid sepgsql_ts_parser_create(const char *parseName, Oid namespaceId, ++ Oid startFunc, Oid tokenFunc, Oid endFunc, ++ Oid headlineFunc, Oid lextypeFunc); ++extern void sepgsql_ts_parser_alter_rename(Oid parseOid, const char *newName); ++extern void sepgsql_ts_parser_drop(Oid parseOid, bool cascade); ++extern void sepgsql_ts_parser_comment(Oid parseOid); ++extern Oid sepgsql_ts_template_create(const char *templateName, ++ Oid namespaceId, ++ Oid initFunc, Oid lexizeFunc); ++extern void sepgsql_ts_template_alter_rename(Oid templateOid, ++ const char *newName); ++extern void sepgsql_ts_template_drop(Oid templateOid, bool cascade); ++extern void sepgsql_ts_template_comment(Oid templateOid); ++ ++/* ++ * fdw.c ++ */ ++extern Oid sepgsql_fdw_create(const char *fdwName, Oid validatorFunc); ++extern void sepgsql_fdw_alter(Oid fdwOid, Oid newValidator); ++extern void sepgsql_fdw_drop(Oid fdwOid, bool cascade); ++extern void sepgsql_fdw_grant(Oid fdwOid); ++ ++extern Oid sepgsql_fserver_create(const char *fservName, Oid fdwOid); ++extern void sepgsql_fserver_alter(Oid fservOid); ++extern void sepgsql_fserver_drop(Oid fservOid, bool cascade); ++extern void sepgsql_fserver_grant(Oid fservOid); ++ ++extern Oid sepgsql_user_mapping_create(Oid mappedRoleId, Oid fservOid); ++extern void sepgsql_user_mapping_alter(Oid umapOid); ++extern void sepgsql_user_mapping_drop(Oid umapOid, bool cascade); ++ ++extern void sepgsql_file_getattr(const char *filename); ++extern void sepgsql_file_read(const char *filename); ++extern void sepgsql_file_write(const char *filename, bool may_create); ++extern void sepgsql_file_append(const char *filename); ++extern void sepgsql_file_unlink(const char *filename); ++extern void sepgsql_file_rename(const char *oldpath, const char *newpath); ++extern void sepgsql_dir_list(const char *dirname); ++ ++/* ++ * row-level access controls ++ */ ++#define SEPGSQL_ROWLV_FILTER 1 ++#define SEPGSQL_ROWLV_ABORT 2 ++#define SEPGSQL_ROWLV_BYPASS 3 ++ ++extern int sepgsql_rowlv_get_mode(void); ++extern int sepgsql_rowlv_set_mode(int new_mode); ++extern bool sepgsql_rowlv_quals(Relation rel, uint32 required, ++ TupleTableSlot *slot, bool abort); ++extern bool sepgsql_rowlv_copyto(Relation rel, HeapTuple tuple); ++extern uint32 sepgsql_rowlv_permissions(RangeTblEntry *rte); ++ ++extern void sepgsql_tuple_insert(Relation rel, HeapTuple tuple); ++extern void sepgsql_tuple_update(Relation rel, ItemPointer otid, HeapTuple newtup); ++ ++#endif /* SEPGSQL_HOOKS_H */ +diff --git a/src/include/sepgsql/sepgsql.h b/src/include/sepgsql/sepgsql.h +new file mode 100644 +index 0000000..5fd7195 +--- /dev/null ++++ b/src/include/sepgsql/sepgsql.h +@@ -0,0 +1,318 @@ ++/* ++ * sepgsql/sepgsql.h ++ * ++ * Header of SE-PostgreSQL Internal ++ * ++ * Copyright (C) 2006-2010, NEC Corporation ++ * KaiGai Kohei ++ */ ++#ifndef SEPGSQL_H ++#define SEPGSQL_H ++ ++#include "utils/snapshot.h" ++ ++/* GUC : sepostgresql */ ++extern int sepostgresql_mode; ++ ++#define SEPGSQL_MODE_DEFAULT 1 ++#define SEPGSQL_MODE_ENFORCING 2 ++#define SEPGSQL_MODE_PERMISSIVE 3 ++#define SEPGSQL_MODE_INTERNAL 4 ++#define SEPGSQL_MODE_DISABLED 5 ++ ++/* GUC: sepostgresql_mcstrans */ ++extern bool sepgsql_mcstrans; ++ ++/* GUC: sepostgresql_debug_audit */ ++extern bool sepgsql_debug_audit; ++ ++/* Objject classes and permissions internally used */ ++enum SepgsqlClasses ++{ ++ SEPG_CLASS_PROCESS = 0, ++ SEPG_CLASS_FILE, ++ SEPG_CLASS_DIR, ++ SEPG_CLASS_LNK_FILE, ++ SEPG_CLASS_CHR_FILE, ++ SEPG_CLASS_BLK_FILE, ++ SEPG_CLASS_SOCK_FILE, ++ SEPG_CLASS_FIFO_FILE, ++ SEPG_CLASS_DB_DATABASE, ++ SEPG_CLASS_DB_SCHEMA, ++ SEPG_CLASS_DB_TABLE, ++ SEPG_CLASS_DB_VIEW, ++ SEPG_CLASS_DB_SEQUENCE, ++ SEPG_CLASS_DB_PROCEDURE, ++ SEPG_CLASS_DB_COLUMN, ++ SEPG_CLASS_DB_TUPLE, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_CLASS_DB_LANGUAGE, ++ SEPG_CLASS_MAX, ++}; ++ ++#define SEPG_PROCESS__TRANSITION (1<<0) ++ ++#define SEPG_FILE__READ (1<<0) ++#define SEPG_FILE__WRITE (1<<1) ++#define SEPG_FILE__CREATE (1<<2) ++#define SEPG_FILE__GETATTR (1<<3) ++#define SEPG_FILE__UNLINK (1<<4) ++#define SEPG_FILE__RENAME (1<<5) ++#define SEPG_FILE__APPEND (1<<6) ++ ++#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_DIR__UNLINK (SEPG_FILE__UNLINK) ++#define SEPG_DIR__RENAME (SEPG_FILE__RENAME) ++#define SEPG_DIR__SEARCH (1<<6) ++#define SEPG_DIR__ADD_NAME (1<<7) ++#define SEPG_DIR__REMOVE_NAME (1<<8) ++#define SEPG_DIR__RMDIR (1<<9) ++#define SEPG_DIR__REPARENT (1<<10) ++ ++#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_LNK_FILE__UNLINK (SEPG_FILE__UNLINK) ++#define SEPG_LNK_FILE__RENAME (SEPG_FILE__RENAME) ++ ++#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_CHR_FILE__UNLINK (SEPG_FILE__UNLINK) ++#define SEPG_CHR_FILE__RENAME (SEPG_FILE__RENAME) ++ ++#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_BLK_FILE__UNLINK (SEPG_FILE__UNLINK) ++#define SEPG_BLK_FILE__RENAME (SEPG_FILE__RENAME) ++ ++#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_SOCK_FILE__UNLINK (SEPG_FILE__UNLINK) ++#define SEPG_SOCK_FILE__RENAME (SEPG_FILE__RENAME) ++ ++#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_FIFO_FILE__UNLINK (SEPG_FILE__UNLINK) ++#define SEPG_FIFO_FILE__RENAME (SEPG_FILE__RENAME) ++ ++#define SEPG_DB_DATABASE__CREATE (1<<0) ++#define SEPG_DB_DATABASE__DROP (1<<1) ++#define SEPG_DB_DATABASE__GETATTR (1<<2) ++#define SEPG_DB_DATABASE__SETATTR (1<<3) ++#define SEPG_DB_DATABASE__RELABELFROM (1<<4) ++#define SEPG_DB_DATABASE__RELABELTO (1<<5) ++#define SEPG_DB_DATABASE__ACCESS (1<<6) ++#define SEPG_DB_DATABASE__LOAD_MODULE (1<<7) ++ ++#define SEPG_DB_SCHEMA__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_SCHEMA__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_SCHEMA__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_SCHEMA__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_SCHEMA__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_SCHEMA__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_SCHEMA__SEARCH (1<<6) ++#define SEPG_DB_SCHEMA__ADD_NAME (1<<7) ++#define SEPG_DB_SCHEMA__REMOVE_NAME (1<<8) ++ ++#define SEPG_DB_TABLE__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_TABLE__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_TABLE__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_TABLE__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_TABLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_TABLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_TABLE__SELECT (1<<6) ++#define SEPG_DB_TABLE__UPDATE (1<<7) ++#define SEPG_DB_TABLE__INSERT (1<<8) ++#define SEPG_DB_TABLE__DELETE (1<<9) ++#define SEPG_DB_TABLE__LOCK (1<<10) ++#define SEPG_DB_TABLE__INDEXON (1<<11) ++ ++#define SEPG_DB_SEQUENCE__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_SEQUENCE__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_SEQUENCE__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_SEQUENCE__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_SEQUENCE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_SEQUENCE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_SEQUENCE__GET_VALUE (1<<6) ++#define SEPG_DB_SEQUENCE__NEXT_VALUE (1<<7) ++#define SEPG_DB_SEQUENCE__SET_VALUE (1<<8) ++ ++#define SEPG_DB_VIEW__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_VIEW__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_VIEW__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_VIEW__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_VIEW__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_VIEW__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_VIEW__EXPAND (1<<6) ++ ++#define SEPG_DB_PROCEDURE__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_PROCEDURE__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_PROCEDURE__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_PROCEDURE__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_PROCEDURE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_PROCEDURE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_PROCEDURE__EXECUTE (1<<6) ++#define SEPG_DB_PROCEDURE__ENTRYPOINT (1<<7) ++#define SEPG_DB_PROCEDURE__INSTALL (1<<8) ++ ++#define SEPG_DB_COLUMN__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_COLUMN__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_COLUMN__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_COLUMN__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_COLUMN__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_COLUMN__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_COLUMN__SELECT (1<<6) ++#define SEPG_DB_COLUMN__UPDATE (1<<7) ++#define SEPG_DB_COLUMN__INSERT (1<<8) ++ ++#define SEPG_DB_TUPLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_TUPLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_TUPLE__SELECT (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_TUPLE__UPDATE (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_TUPLE__INSERT (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_TUPLE__DELETE (SEPG_DB_DATABASE__DROP) ++ ++#define SEPG_DB_BLOB__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_BLOB__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_BLOB__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_BLOB__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_BLOB__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_BLOB__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_BLOB__READ (1<<6) ++#define SEPG_DB_BLOB__WRITE (1<<7) ++#define SEPG_DB_BLOB__IMPORT (1<<8) ++#define SEPG_DB_BLOB__EXPORT (1<<9) ++ ++#define SEPG_DB_LANGUAGE__CREATE (SEPG_DB_DATABASE__CREATE) ++#define SEPG_DB_LANGUAGE__DROP (SEPG_DB_DATABASE__DROP) ++#define SEPG_DB_LANGUAGE__GETATTR (SEPG_DB_DATABASE__GETATTR) ++#define SEPG_DB_LANGUAGE__SETATTR (SEPG_DB_DATABASE__SETATTR) ++#define SEPG_DB_LANGUAGE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) ++#define SEPG_DB_LANGUAGE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) ++#define SEPG_DB_LANGUAGE__IMPLEMENTE (1<<6) ++#define SEPG_DB_LANGUAGE__EXECUTE (1<<7) ++ ++/* ++ * sepgsql_sid_t : alternative representation of security context ++ */ ++typedef struct { ++ Oid relid; ++ Oid secid; ++} sepgsql_sid_t; ++ ++struct av_decision; ++ ++/* ++ * selinux.c ++ */ ++extern bool sepgsql_is_enabled(void); ++extern bool sepgsql_get_enforce(void); ++extern const char *sepgsql_show_mode(void); ++ ++extern Size sepgsql_shmem_size(void); ++ ++extern void sepgsql_audit_log(bool denied, ++ char *scontext, ++ char *tcontext, ++ uint16 tclass, ++ uint32 audited, ++ const char *audit_name); ++extern void sepgsql_compute_avd(char *scontext, ++ char *tcontext, ++ uint16 tclass, ++ struct av_decision *avd); ++extern bool sepgsql_compute_perms(char *scontext, ++ char *tcontext, ++ uint16 tclass, ++ uint32 required, ++ const char *audit_name, ++ bool abort); ++extern char *sepgsql_compute_create(char *scontext, ++ char *tcontext, ++ uint16 tclass); ++extern bool sepgsql_client_perms(sepgsql_sid_t tsid, ++ uint16 tclass, ++ uint32 required, ++ const char *audit_name, ++ bool abort); ++extern sepgsql_sid_t sepgsql_client_create_secid(sepgsql_sid_t tsid, ++ uint16 tclass, ++ Oid nrelid); ++extern char *sepgsql_client_create_label(sepgsql_sid_t tsid, ++ uint16 tclass); ++extern void sepgsql_avc_worker_main(void); ++ ++/* ++ * avc.c ++ */ ++extern Size sepgsql_shmem_size(void); ++extern void sepgsql_avc_init(void); ++extern void sepgsql_avc_switch(const char *scontext); ++ ++ ++/* ++ * label.c ++ */ ++extern sepgsql_sid_t sepgsql_move_secid(Oid dst_relid, sepgsql_sid_t ssid); ++ ++extern sepgsql_sid_t sepgsql_get_default_database_secid(Oid templateOid); ++extern sepgsql_sid_t sepgsql_get_default_schema_secid(Oid databaseOid); ++extern sepgsql_sid_t sepgsql_get_default_table_secid(Oid namespaceOid); ++extern sepgsql_sid_t sepgsql_get_default_sequence_secid(Oid namespaceOid); ++extern sepgsql_sid_t sepgsql_get_default_view_secid(Oid namespaceOid); ++extern sepgsql_sid_t sepgsql_get_default_proc_secid(Oid namespaceOid); ++extern sepgsql_sid_t sepgsql_get_default_column_secid(Oid tableOid); ++extern sepgsql_sid_t sepgsql_get_default_tuple_secid(Oid tableOid); ++extern sepgsql_sid_t sepgsql_get_default_blob_secid(Oid databaseOid); ++extern Oid sepgsql_get_default_secid(Relation rel, HeapTuple tuple); ++ ++extern void sepgsql_initial_labeling(void); ++ ++extern char *sepgsql_mcstrans_out(char *label); ++extern char *sepgsql_mcstrans_in(char *label); ++extern char *sepgsql_rawlabel_out(char *label); ++extern char *sepgsql_rawlabel_in(char *label); ++ ++/* ++ * sepgsql_(object)_common ++ */ ++extern bool sepgsql_database_common(Oid datOid, uint32 required, bool abort); ++extern bool sepgsql_schema_common(Oid nspOid, uint32 required, bool abort); ++extern bool sepgsql_relation_common(Oid relOid, uint32 required, bool abort); ++extern bool sepgsql_attribute_common(Oid relOid, AttrNumber attno, ++ uint32 required, bool abort); ++extern bool sepgsql_proc_common(Oid procOid, uint32 required, bool abort); ++extern bool sepgsql_type_common(Oid typeOid, uint32 required, bool abort); ++extern bool sepgsql_cast_common(Oid srcTypeOid, Oid dstTypeOid, ++ uint32 required, bool abort); ++extern bool sepgsql_tablespace_common(Oid tspaceOid, uint32 required, bool abort); ++extern bool sepgsql_operator_common(Oid operOid, uint32 required, bool abort); ++extern bool sepgsql_opclass_common(Oid opcOid, uint32 required, bool abort); ++extern bool sepgsql_opfamily_common(Oid opfOid, uint32 required, bool abort); ++extern bool sepgsql_role_common(Oid roleOid, uint32 required, bool abort); ++extern bool sepgsql_largeobejct_common(Oid loid, Snapshot snapshot, ++ uint32 required, bool abort); ++extern bool sepgsql_conversion_common(Oid convOid, uint32 required, bool abort); ++extern bool sepgsql_largeobject_common(Oid loid, Snapshot snapshot, ++ uint32 required, bool abort); ++extern bool sepgsql_ts_config_common(Oid confOid, uint32 required, bool abort); ++extern bool sepgsql_ts_dict_common(Oid dictOid, uint32 required, bool abort); ++extern bool sepgsql_ts_parser_common(Oid parseOid, uint32 required, bool abort); ++extern bool sepgsql_ts_template_common(Oid templateOid, uint32 required, bool abort); ++extern bool sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort); ++extern bool sepgsql_fserver_common(Oid fservOid, uint32 required, bool abort); ++extern bool sepgsql_user_mapping_common(Oid umapOid, uint32 required, bool abort); ++ ++#endif /* SEPGSQL_H */ +diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h +index 43a61f3..6385b5c 100644 +--- a/src/include/storage/large_object.h ++++ b/src/include/storage/large_object.h +@@ -70,7 +70,7 @@ typedef struct LargeObjectDesc + + /* inversion stuff in inv_api.c */ + extern void close_lo_relation(bool isCommit); +-extern Oid inv_create(Oid lobjId); ++extern Oid inv_create(Oid lobjId, Oid securityId); + extern LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt); + extern void inv_close(LargeObjectDesc *obj_desc); + extern int inv_drop(Oid lobjId); +diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h +index 4eece8b..4176eac 100644 +--- a/src/include/storage/lwlock.h ++++ b/src/include/storage/lwlock.h +@@ -70,6 +70,7 @@ typedef enum LWLockId + RelationMappingLock, + AsyncCtlLock, + AsyncQueueLock, ++ SepgsqlAvcLock, + /* Individual lock IDs end here */ + FirstBufMappingLock, + FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, +diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h +index c22c65a..5b94283 100644 +--- a/src/include/utils/guc.h ++++ b/src/include/utils/guc.h +@@ -166,6 +166,7 @@ extern bool log_btree_build_stats; + + extern PGDLLIMPORT bool check_function_bodies; + extern bool default_with_oids; ++extern bool default_with_secids; + extern bool SQL_inheritance; + + extern int log_min_error_statement; +diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h +index 2f19e5c..6744782 100644 +--- a/src/include/utils/syscache.h ++++ b/src/include/utils/syscache.h +@@ -101,6 +101,8 @@ extern bool SearchSysCacheExists(int cacheId, + Datum key1, Datum key2, Datum key3, Datum key4); + extern Oid GetSysCacheOid(int cacheId, + Datum key1, Datum key2, Datum key3, Datum key4); ++extern Oid GetSysCacheSecid(int cacheId, ++ Datum key1, Datum key2, Datum key3, Datum key4); + + extern HeapTuple SearchSysCacheAttName(Oid relid, const char *attname); + extern HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname); +@@ -154,6 +156,15 @@ extern struct catclist *SearchSysCacheList(int cacheId, int nkeys, + #define GetSysCacheOid4(cacheId, key1, key2, key3, key4) \ + GetSysCacheOid(cacheId, key1, key2, key3, key4) + ++#define GetSysCacheSecid1(cacheId, key1) \ ++ GetSysCacheSecid(cacheId, key1, 0, 0, 0) ++#define GetSysCacheSecid2(cacheId, key1, key2) \ ++ GetSysCacheSecid(cacheId, key1, key2, 0, 0) ++#define GetSysCacheSecid3(cacheId, key1, key2, key3) \ ++ GetSysCacheSecid(cacheId, key1, key2, key3, 0) ++#define GetSysCacheSecid4(cacheId, key1, key2, key3, key4) \ ++ GetSysCacheSecid(cacheId, key1, key2, key3, key4) ++ + #define SearchSysCacheList1(cacheId, key1) \ + SearchSysCacheList(cacheId, 1, key1, 0, 0, 0) + #define SearchSysCacheList2(cacheId, key1, key2) \ +diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c +index 656ea73..2ce4e94 100644 +--- a/src/pl/plpgsql/src/pl_comp.c ++++ b/src/pl/plpgsql/src/pl_comp.c +@@ -1936,7 +1936,7 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars) + + row = palloc0(sizeof(PLpgSQL_row)); + row->dtype = PLPGSQL_DTYPE_ROW; +- row->rowtupdesc = CreateTemplateTupleDesc(numvars, false); ++ row->rowtupdesc = CreateTemplateTupleDesc(numvars, false, false); + row->nfields = numvars; + row->fieldnames = palloc(numvars * sizeof(char *)); + row->varnos = palloc(numvars * sizeof(int)); +diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out +index 1d9e110..9596b0b 100644 +--- a/src/test/regress/expected/sanity_check.out ++++ b/src/test/regress/expected/sanity_check.out +@@ -114,6 +114,7 @@ SELECT relname, relhasindex + pg_pltemplate | t + pg_proc | t + pg_rewrite | t ++ pg_seclabel | t + pg_shdepend | t + pg_shdescription | t + pg_statistic | t +@@ -153,7 +154,7 @@ SELECT relname, relhasindex + timetz_tbl | f + tinterval_tbl | f + varchar_tbl | f +-(142 rows) ++(143 rows) + + -- + -- another sanity check: every system catalog that has OIDs should have diff --git a/sepostgresql-fedora-prefix.patch b/sepostgresql-fedora-prefix.patch index a9dd24b..98cd3b2 100644 --- a/sepostgresql-fedora-prefix.patch +++ b/sepostgresql-fedora-prefix.patch @@ -1,8 +1,8 @@ -Index: sepgsql/src/Makefile.global.in -=================================================================== ---- sepgsql/src/Makefile.global.in (revision 2237) -+++ sepgsql/src/Makefile.global.in (working copy) -@@ -75,14 +75,14 @@ +diff --git a/src/Makefile.global.in b/src/Makefile.global.in +index 0e3bed5..aee6064 100644 +--- a/src/Makefile.global.in ++++ b/src/Makefile.global.in +@@ -74,14 +74,14 @@ bindir := @bindir@ datadir := @datadir@ ifeq "$(findstring pgsql, $(datadir))" "" ifeq "$(findstring postgres, $(datadir))" "" @@ -19,7 +19,7 @@ Index: sepgsql/src/Makefile.global.in endif endif -@@ -91,7 +91,7 @@ +@@ -90,7 +90,7 @@ libdir := @libdir@ pkglibdir = $(libdir) ifeq "$(findstring pgsql, $(pkglibdir))" "" ifeq "$(findstring postgres, $(pkglibdir))" "" @@ -28,7 +28,7 @@ Index: sepgsql/src/Makefile.global.in endif endif -@@ -100,7 +100,7 @@ +@@ -99,7 +99,7 @@ includedir := @includedir@ pkgincludedir = $(includedir) ifeq "$(findstring pgsql, $(pkgincludedir))" "" ifeq "$(findstring postgres, $(pkgincludedir))" "" @@ -37,7 +37,7 @@ Index: sepgsql/src/Makefile.global.in endif endif -@@ -109,7 +109,7 @@ +@@ -108,7 +108,7 @@ mandir := @mandir@ docdir := @docdir@ ifeq "$(findstring pgsql, $(docdir))" "" ifeq "$(findstring postgres, $(docdir))" "" @@ -46,37 +46,55 @@ Index: sepgsql/src/Makefile.global.in endif endif -Index: sepgsql/src/bin/pg_ctl/pg_ctl.c -=================================================================== ---- sepgsql/src/bin/pg_ctl/pg_ctl.c (revision 2237) -+++ sepgsql/src/bin/pg_ctl/pg_ctl.c (working copy) -@@ -643,7 +643,7 @@ - - postmaster_path = pg_malloc(MAXPGPATH); - -- if ((ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR, -+ if ((ret = find_other_exec(argv0, "sepostgres", PG_BACKEND_VERSIONSTR, - postmaster_path)) < 0) - { - char full_path[MAXPGPATH]; -Index: sepgsql/src/bin/initdb/initdb.c -=================================================================== ---- sepgsql/src/bin/initdb/initdb.c (revision 2237) -+++ sepgsql/src/bin/initdb/initdb.c (working copy) -@@ -2763,7 +2763,7 @@ - sprintf(pgdenv, "PGDATA=%s", pg_data); - putenv(pgdenv); +diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c +index 497bdf0..54908f8 100644 +--- a/src/bin/initdb/initdb.c ++++ b/src/bin/initdb/initdb.c +@@ -2722,7 +2722,7 @@ main(int argc, char *argv[]) + */ + putenv("TZ=GMT"); - if ((ret = find_other_exec(argv[0], "postgres", PG_BACKEND_VERSIONSTR, + if ((ret = find_other_exec(argv[0], "sepostgres", PG_BACKEND_VERSIONSTR, backend_exec)) < 0) { char full_path[MAXPGPATH]; -Index: sepgsql/src/bin/pg_dump/pg_dumpall.c -=================================================================== ---- sepgsql/src/bin/pg_dump/pg_dumpall.c (revision 2237) -+++ sepgsql/src/bin/pg_dump/pg_dumpall.c (working copy) -@@ -157,7 +157,7 @@ +diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c +index 814ce97..2550e57 100644 +--- a/src/bin/pg_ctl/pg_ctl.c ++++ b/src/bin/pg_ctl/pg_ctl.c +@@ -654,7 +654,7 @@ do_init(void) + char cmd[MAXPGPATH]; + + if (exec_path == NULL) +- exec_path = find_other_exec_or_die(argv0, "initdb", "initdb (PostgreSQL) " PG_VERSION "\n"); ++ exec_path = find_other_exec_or_die(argv0, "initdb.sepgsql", "initdb (PostgreSQL) " PG_VERSION "\n"); + + if (pgdata_opt == NULL) + pgdata_opt = ""; +@@ -699,7 +699,7 @@ do_start(void) + pgdata_opt = ""; + + if (exec_path == NULL) +- exec_path = find_other_exec_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR); ++ exec_path = find_other_exec_or_die(argv0, "sepostgres", PG_BACKEND_VERSIONSTR); + + #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE) + if (allow_core_files) +@@ -1069,7 +1069,7 @@ pgwin32_CommandLine(bool registration) + } + else + { +- ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR, ++ ret = find_other_exec(argv0, "sepostgres", PG_BACKEND_VERSIONSTR, + cmdLine); + if (ret != 0) + { +diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c +index 83f1678..a0a9444 100644 +--- a/src/bin/pg_dump/pg_dumpall.c ++++ b/src/bin/pg_dump/pg_dumpall.c +@@ -156,7 +156,7 @@ main(int argc, char *argv[]) } } diff --git a/sepostgresql.spec b/sepostgresql.spec index bdc0765..c018344 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -8,22 +8,24 @@ %define selinux_policy_stores targeted mls %{!?ssl:%define ssl 1} +%{!?beta:%define beta .beta1} Summary: Security Enhanced PostgreSQL Name: sepostgresql -Version: 8.4.3 -Release: 2582%{?dist} -License: BSD +Version: 9.0.0 +Release: 20100511%{?beta}%{?dist} +License: PostgreSQL Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ Buildroot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) -Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.bz2 +#XXX - to be revert later +#Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.bz2 +Source0: postgresql-9.0beta1.tar.gz Source1: sepostgresql.init Source2: sepostgresql.8 Source3: sepostgresql.logrotate Patch0: sepostgresql-fedora-prefix.patch -Patch1: pgsql-01-8.4-blobs.patch -Patch2: pgsql-02-8.4-sepgsql.patch +Patch1: sepostgresql-9.0-fullset.patch BuildRequires: perl glibc-devel bison flex readline-devel zlib-devel >= 1.0.4 BuildRequires: checkpolicy libselinux-devel >= 2.0.80 BuildRequires: selinux-policy >= 3.6.8 @@ -34,7 +36,6 @@ Requires(pre): shadow-utils Requires(post): policycoreutils /sbin/chkconfig Requires(preun): /sbin/chkconfig /sbin/service Requires(postun): policycoreutils -Requires: postgresql-server = %{version} Requires: policycoreutils >= 2.0.16 libselinux >= 2.0.80 Requires: selinux-policy >= 3.6.8 Requires: tzdata logrotate @@ -48,10 +49,11 @@ the operating system. SE-PostgreSQL works as a userspace reference monitor to check any SQL query. %prep -%setup -q -n postgresql-%{version} +#XXX - to be revert later +#%setup -q -n postgresql-%{version} +%setup -q -n postgresql-9.0beta1 %patch0 -p1 %patch1 -p1 -%patch2 -p1 %build CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS @@ -65,36 +67,39 @@ CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS %endif --enable-debug \ --enable-cassert \ - --libdir=%{_libdir}/pgsql \ - --datadir=%{_datadir}/sepgsql \ --with-system-tzdata=/usr/share/zoneinfo # parallel build, if possible +rm -f src/Makefile.custom make %{?_smp_mflags} -touch src/backend/security/sepgsql/policy/sepostgresql-devel.fc -make -C src/backend/security/sepgsql/policy %install rm -rf %{buildroot} make DESTDIR=%{buildroot} install -install -d %{buildroot}%{_datadir}/selinux/packages -install -p -m 644 src/backend/security/sepgsql/policy/sepostgresql-devel.pp \ - %{buildroot}%{_datadir}/selinux/packages - # avoid to conflict with native postgresql package mv %{buildroot}%{_bindir} %{buildroot}%{_bindir}.orig -install -d %{buildroot}%{_bindir} +install -d %{buildroot}%{_bindir}/ mv %{buildroot}%{_bindir}.orig/initdb %{buildroot}%{_bindir}/initdb.sepgsql mv %{buildroot}%{_bindir}.orig/pg_ctl %{buildroot}%{_bindir}/sepg_ctl mv %{buildroot}%{_bindir}.orig/postgres %{buildroot}%{_bindir}/sepostgres mv %{buildroot}%{_bindir}.orig/pg_dump %{buildroot}%{_bindir}/sepg_dump mv %{buildroot}%{_bindir}.orig/pg_dumpall %{buildroot}%{_bindir}/sepg_dumpall +mv %{buildroot}%{_bindir}.orig/pg_restore %{buildroot}%{_bindir}/sepg_restore + +mv %{buildroot}%{_libdir} %{buildroot}%{_libdir}.orig +install -d %{buildroot}%{_libdir}/sepgsql +mv %{buildroot}%{_libdir}.orig/sepgsql/dict_snowball.so \ + %{buildroot}%{_libdir}.orig/sepgsql/plpgsql.so \ + %{buildroot}%{_libdir}.orig/sepgsql/*_and_*.so \ + %{buildroot}%{_libdir}.orig/sepgsql/euc2004_sjis2004.so \ + %{buildroot}%{_libdir}.orig/sepgsql/libpqwalreceiver.so \ + %{buildroot}%{_libdir}/sepgsql # remove unnecessary files rm -rf %{buildroot}%{_bindir}.orig -rm -rf %{buildroot}%{_libdir} +rm -rf %{buildroot}%{_libdir}.orig rm -rf %{buildroot}%{_includedir} rm -rf %{buildroot}%{_datadir}/doc rm -rf %{buildroot}%{_datadir}/sepgsql/timezone @@ -123,24 +128,13 @@ rm -rf %{buildroot} %pre getent group sepgsql >/dev/null || groupadd -r sepgsql getent passwd sepgsql >/dev/null || \ - useradd -r -g sepgsql -d %{_localstatedir}/lib/sepgsql -s /bin/bash \ - -c "SE-PostgreSQL server" sepgsql + useradd -r -g sepgsql -s /bin/bash -c "SE-PostgreSQL" sepgsql exit 0 %post /sbin/chkconfig --add %{name} /sbin/ldconfig -for store in %{selinux_policy_stores} -do - # clean up legacy policy module (now it is unnecessary) - %{_sbindir}/semodule -s ${store} -r sepostgresql >& /dev/null || : - if %{_sbindir}/semodule -s ${store} -l 2>/dev/null | grep -Eq "^sepostgresql-devel"; then - %{_sbindir}/semodule -s ${store} \ - -i %{_datadir}/selinux/packages/sepostgresql-devel.pp >& /dev/null || : - fi -done - # Fix up non-standard file contexts /sbin/fixfiles -R %{name} restore || : /sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : @@ -157,12 +151,9 @@ if [ $1 -ge 1 ]; then # rpm -U case /sbin/service %{name} condrestart >/dev/null 2>&1 || : fi if [ $1 -eq 0 ]; then # rpm -e case - for store in %{selinux_policy_stores} - do - %{_sbindir}/semodule -s ${store} -r sepostgresql-devel >& /dev/null || : - done /sbin/fixfiles -R %{name} restore || : - test -d %{_localstatedir}/lib/sepgsql && /sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : + test -d %{_localstatedir}/lib/sepgsql && \ + /sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : fi %files @@ -175,6 +166,8 @@ fi %{_bindir}/sepostgres %{_bindir}/sepg_dump %{_bindir}/sepg_dumpall +%{_bindir}/sepg_restore +%{_libdir}/sepgsql/*.so %{_mandir}/man8/sepostgresql.* %dir %{_datadir}/sepgsql %{_datadir}/sepgsql/postgres.bki @@ -188,12 +181,17 @@ fi %{_datadir}/sepgsql/conversion_create.sql %{_datadir}/sepgsql/information_schema.sql %{_datadir}/sepgsql/sql_features.txt -%attr(644,root,root) %{_datadir}/selinux/packages/sepostgresql-devel.pp %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/data %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog +* Tue May 11 2010 KaiGai Kohei - 9.0.0-20100511 +- upgrade base version to 9.0beta1 + +* Sun Apr 4 2010 KaiGai Kohei - 9.0.0-20100404 +- upgrade base version 8.4.3->9.0alpha5 + * Thu Mar 18 2010 KaiGai Kohei - 8.4.3-2582 - upgrade base version 8.4.2->8.4.3 diff --git a/sources b/sources index b1c5788..4ba8bc6 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -7f70e7b140fb190f268837255582b07e postgresql-8.4.3.tar.bz2 +acaa0852b970524b91ea9a2fd0fa61c8 postgresql-9.0beta1.tar.gz From 1dfef69255fb82624879e778a7296e8c39238d95 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Mon, 24 May 2010 13:03:23 +0000 Subject: [PATCH 11/24] add audit-libs-devel on BuildRequires --- sepostgresql.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sepostgresql.spec b/sepostgresql.spec index c018344..f48bd42 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -13,7 +13,7 @@ Summary: Security Enhanced PostgreSQL Name: sepostgresql Version: 9.0.0 -Release: 20100511%{?beta}%{?dist} +Release: 20100523%{?beta}%{?dist} License: PostgreSQL Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ @@ -27,7 +27,7 @@ Source3: sepostgresql.logrotate Patch0: sepostgresql-fedora-prefix.patch Patch1: sepostgresql-9.0-fullset.patch BuildRequires: perl glibc-devel bison flex readline-devel zlib-devel >= 1.0.4 -BuildRequires: checkpolicy libselinux-devel >= 2.0.80 +BuildRequires: checkpolicy libselinux-devel >= 2.0.80 audit-libs-devel BuildRequires: selinux-policy >= 3.6.8 %if %{ssl} BuildRequires: openssl-devel From 3f75f38b7bbaba8db6f397086287744a9bd66c3d Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Mon, 24 May 2010 13:04:25 +0000 Subject: [PATCH 12/24] add audit-libs-devel on BuildRequires --- sepostgresql.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sepostgresql.spec b/sepostgresql.spec index c018344..f48bd42 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -13,7 +13,7 @@ Summary: Security Enhanced PostgreSQL Name: sepostgresql Version: 9.0.0 -Release: 20100511%{?beta}%{?dist} +Release: 20100523%{?beta}%{?dist} License: PostgreSQL Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ @@ -27,7 +27,7 @@ Source3: sepostgresql.logrotate Patch0: sepostgresql-fedora-prefix.patch Patch1: sepostgresql-9.0-fullset.patch BuildRequires: perl glibc-devel bison flex readline-devel zlib-devel >= 1.0.4 -BuildRequires: checkpolicy libselinux-devel >= 2.0.80 +BuildRequires: checkpolicy libselinux-devel >= 2.0.80 audit-libs-devel BuildRequires: selinux-policy >= 3.6.8 %if %{ssl} BuildRequires: openssl-devel From f5d387116fe894348ee6ec5b4a124ffeafe542cd Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 29 Jul 2010 12:35:20 +0000 Subject: [PATCH 13/24] dist-git conversion --- .cvsignore => .gitignore | 0 Makefile | 21 --------------------- branch | 1 - 3 files changed, 22 deletions(-) rename .cvsignore => .gitignore (100%) delete mode 100644 Makefile delete mode 100644 branch diff --git a/.cvsignore b/.gitignore similarity index 100% rename from .cvsignore rename to .gitignore diff --git a/Makefile b/Makefile deleted file mode 100644 index 59ecad3..0000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# Makefile for source rpm: sepostgresql -# $Id$ -NAME := sepostgresql -SPECFILE = $(firstword $(wildcard *.spec)) - -define find-makefile-common -for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$d/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done -endef - -MAKEFILE_COMMON := $(shell $(find-makefile-common)) - -ifeq ($(MAKEFILE_COMMON),) -# attept a checkout -define checkout-makefile-common -test -f CVS/Root && { cvs -Q -d $$(cat CVS/Root) checkout common && echo "common/Makefile.common" ; } || { echo "ERROR: I can't figure out how to checkout the 'common' module." ; exit -1 ; } >&2 -endef - -MAKEFILE_COMMON := $(shell $(checkout-makefile-common)) -endif - -include $(MAKEFILE_COMMON) diff --git a/branch b/branch deleted file mode 100644 index baa94ef..0000000 --- a/branch +++ /dev/null @@ -1 +0,0 @@ -F-13 From 16bcec5b1c4cd5c0a5f13998f7f6166d9357bd89 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 29 Jul 2010 12:36:03 +0000 Subject: [PATCH 14/24] dist-git conversion --- .cvsignore => .gitignore | 0 Makefile | 21 --------------------- 2 files changed, 21 deletions(-) rename .cvsignore => .gitignore (100%) delete mode 100644 Makefile diff --git a/.cvsignore b/.gitignore similarity index 100% rename from .cvsignore rename to .gitignore diff --git a/Makefile b/Makefile deleted file mode 100644 index 59ecad3..0000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# Makefile for source rpm: sepostgresql -# $Id$ -NAME := sepostgresql -SPECFILE = $(firstword $(wildcard *.spec)) - -define find-makefile-common -for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$d/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done -endef - -MAKEFILE_COMMON := $(shell $(find-makefile-common)) - -ifeq ($(MAKEFILE_COMMON),) -# attept a checkout -define checkout-makefile-common -test -f CVS/Root && { cvs -Q -d $$(cat CVS/Root) checkout common && echo "common/Makefile.common" ; } || { echo "ERROR: I can't figure out how to checkout the 'common' module." ; exit -1 ; } >&2 -endef - -MAKEFILE_COMMON := $(shell $(checkout-makefile-common)) -endif - -include $(MAKEFILE_COMMON) From 818aa330e06390c6093e3af76feabb08cddf62f9 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Tue, 5 Oct 2010 10:07:52 +0900 Subject: [PATCH 15/24] upgrade base version to 9.0.0 --- sepostgresql.init | 6 +++--- sepostgresql.spec | 14 ++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/sepostgresql.init b/sepostgresql.init index 866aaba..de34def 100644 --- a/sepostgresql.init +++ b/sepostgresql.init @@ -7,9 +7,6 @@ # pidfile: /var/run/postmaster.pid #--------------------------------------------------------------------- -PGVERSION="8.4.1" -PGMAJORVERSION=`echo "$PGVERSION" | sed 's/^\([0-9]*\.[0-9a-z]*\).*$/\1/'` - # source function library . /etc/rc.d/init.d/functions @@ -22,6 +19,9 @@ if [ ${NAME:0:1} = "S" -o ${NAME:0:1} = "K" ]; then NAME=${NAME:3} fi +PGVERSION=`rpm -q --queryformat='%{version}' ${NAME}` +PGMAJORVERSION=`echo "$PGVERSION" | sed 's/^\([0-9]*\.[0-9a-z]*\).*$/\1/'` + # set defaults for configurable variables SEPGSQL_BIN="/usr/bin" SEPGSQL_DATA="/var/lib/sepgsql/data" diff --git a/sepostgresql.spec b/sepostgresql.spec index f48bd42..05ea0e8 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -8,19 +8,16 @@ %define selinux_policy_stores targeted mls %{!?ssl:%define ssl 1} -%{!?beta:%define beta .beta1} Summary: Security Enhanced PostgreSQL Name: sepostgresql Version: 9.0.0 -Release: 20100523%{?beta}%{?dist} +Release: 20101005%{?dist} License: PostgreSQL Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ Buildroot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) -#XXX - to be revert later -#Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.bz2 -Source0: postgresql-9.0beta1.tar.gz +Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.gz Source1: sepostgresql.init Source2: sepostgresql.8 Source3: sepostgresql.logrotate @@ -49,9 +46,7 @@ the operating system. SE-PostgreSQL works as a userspace reference monitor to check any SQL query. %prep -#XXX - to be revert later -#%setup -q -n postgresql-%{version} -%setup -q -n postgresql-9.0beta1 +%setup -q -n postgresql-%{version} %patch0 -p1 %patch1 -p1 @@ -186,6 +181,9 @@ fi %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog +* Tue Oct 5 2010 KaiGai Kohei - 9.0.0-20101005 +- upgrade base version to 9.0.0 + * Tue May 11 2010 KaiGai Kohei - 9.0.0-20100511 - upgrade base version to 9.0beta1 From aaf1f7379ec14cbe54e7ad22126e5cb809ae3f38 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Tue, 5 Oct 2010 10:10:56 +0900 Subject: [PATCH 16/24] upgrade base version to 9.0.0 --- sepostgresql.init | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sepostgresql.init b/sepostgresql.init index f0b27ee..bc668c4 100644 --- a/sepostgresql.init +++ b/sepostgresql.init @@ -7,12 +7,6 @@ # pidfile: /var/run/postmaster.pid #--------------------------------------------------------------------- -<<<<<<< HEAD -PGVERSION="`/bin/rpm --queryformat=%{version} -q sepostgresql`" -PGMAJORVERSION=`echo "$PGVERSION" | sed 's/^\([0-9]*\.[0-9a-z]*\).*$/\1/'` - -======= ->>>>>>> 818aa330e06390c6093e3af76feabb08cddf62f9 # source function library . /etc/rc.d/init.d/functions From e3e0f05cda5019066a26c33c001b609e4161b9cc Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Tue, 5 Oct 2010 14:51:11 +0900 Subject: [PATCH 17/24] upload postgresql-9.0.0.tar.gz --- .gitignore | 1 + sources | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 15f7b39..07eb4f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ postgresql-9.0beta1.tar.gz +/postgresql-9.0.0.tar.gz diff --git a/sources b/sources index 4ba8bc6..f048e2f 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -acaa0852b970524b91ea9a2fd0fa61c8 postgresql-9.0beta1.tar.gz +9443b3b9c95a48d5e0713feafc209adc postgresql-9.0.0.tar.gz From 7fe72995becdd1a04635a483827e7f03735bcdee Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Tue, 5 Oct 2010 15:01:10 +0900 Subject: [PATCH 18/24] fix specfile --- sepostgresql.spec | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sepostgresql.spec b/sepostgresql.spec index e31fa20..05ea0e8 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -46,9 +46,7 @@ the operating system. SE-PostgreSQL works as a userspace reference monitor to check any SQL query. %prep -#XXX - to be revert later -#%setup -q -n postgresql-%{version} -%setup -q -n postgresql-9.0beta1 +%setup -q -n postgresql-%{version} %patch0 -p1 %patch1 -p1 From 73525530ccf8ccfb853b08bec059669a88bcf21f Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Tue, 5 Oct 2010 21:40:53 +0900 Subject: [PATCH 19/24] fixup sepostgresql-9.0-fullset.patch (rebased to the v9.0.0) --- sepostgresql-9.0-fullset.patch | 1207 +++++++++++++------------------- 1 file changed, 500 insertions(+), 707 deletions(-) diff --git a/sepostgresql-9.0-fullset.patch b/sepostgresql-9.0-fullset.patch index ae16554..01b861d 100644 --- a/sepostgresql-9.0-fullset.patch +++ b/sepostgresql-9.0-fullset.patch @@ -1,8 +1,8 @@ diff --git a/configure b/configure -index b91de59..b44a6be 100755 +index 7c54dc6..bb7e49c 100755 --- a/configure +++ b/configure -@@ -707,6 +707,7 @@ LDFLAGS_SL +@@ -708,6 +708,7 @@ LDFLAGS_EX ELF_SYS EGREP GREP @@ -10,7 +10,7 @@ index b91de59..b44a6be 100755 with_zlib with_system_tzdata with_libxslt -@@ -842,6 +843,7 @@ with_libxml +@@ -843,6 +844,7 @@ with_libxml with_libxslt with_system_tzdata with_zlib @@ -18,7 +18,7 @@ index b91de59..b44a6be 100755 with_gnu_ld enable_largefile enable_float4_byval -@@ -1498,6 +1500,7 @@ Optional Features: +@@ -1500,6 +1502,7 @@ Optional Features: --enable-depend turn on automatic dependency tracking --enable-cassert enable assertion checks (for debugging) --disable-thread-safety disable thread-safety in client libraries @@ -26,7 +26,7 @@ index b91de59..b44a6be 100755 --disable-largefile omit support for large files --disable-float4-byval disable float4 passed by value --disable-float8-byval disable float8 passed by value -@@ -5608,6 +5611,201 @@ fi +@@ -5611,6 +5614,201 @@ fi # @@ -229,7 +229,7 @@ index b91de59..b44a6be 100755 # diff --git a/configure.in b/configure.in -index b2fa682..5797ff5 100644 +index d7fec2c..cc3b119 100644 --- a/configure.in +++ b/configure.in @@ -755,6 +755,19 @@ PGAC_ARG_BOOL(with, zlib, yes, @@ -253,7 +253,7 @@ index b2fa682..5797ff5 100644 # diff --git a/contrib/adminpack/adminpack.c b/contrib/adminpack/adminpack.c -index 13e04b2..c613826 100644 +index 5271ef6..397786d 100644 --- a/contrib/adminpack/adminpack.c +++ b/contrib/adminpack/adminpack.c @@ -22,6 +22,7 @@ @@ -342,208 +342,11 @@ index 13e04b2..c613826 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime", TIMESTAMPOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename", -diff --git a/contrib/sepgtest/sepgtest b/contrib/sepgtest/sepgtest -new file mode 100755 -index 0000000..30e7cb9 ---- /dev/null -+++ b/contrib/sepgtest/sepgtest -@@ -0,0 +1,45 @@ -+#!/bin/sh -+ -+SEPGTEST=`basename $0` -+PSQL=`which psql` -+DIFF=`which diff` -+RUNCON=`which runcon` -+ -+DBUSER="" -+DBHOST="" -+DBPORT="" -+DBNAME="testdb" -+ -+usage() -+{ -+ echo "${SEPGTEST}: [-u ] [-h ] [-p ] [-d ]" -+ exit 1 -+} -+ -+# parse options -+CMD_OPTS=`getopt -n "${SEPGTEST}" "u:h:p:d:" $*` || usage -+eval set -- $CMD_OPTS -+while [ -n "$1" ]; -+do -+ case $1 in -+ -u) -+ DBUSER="-U $2"; shift 2;; -+ -h) -+ DBHOST="-h $2"; shift 2;; -+ -p) -+ DBPORT="-p $2"; shift 2;; -+ -d) -+ DBNAME="$2"; shift 2;; -+ --) -+ shift; break;; -+ *) -+ usage; shift 2;; -+ esac -+done -+ -+PSQL_OPTS="${PSQL} ${DBUSER} ${DBHOST} ${DBPORT} ${DBNAME}" -+ -+# check environment -+ -+ -+ -diff --git a/contrib/sepgtest/sepgtest.in b/contrib/sepgtest/sepgtest.in -new file mode 100755 -index 0000000..30e7cb9 ---- /dev/null -+++ b/contrib/sepgtest/sepgtest.in -@@ -0,0 +1,45 @@ -+#!/bin/sh -+ -+SEPGTEST=`basename $0` -+PSQL=`which psql` -+DIFF=`which diff` -+RUNCON=`which runcon` -+ -+DBUSER="" -+DBHOST="" -+DBPORT="" -+DBNAME="testdb" -+ -+usage() -+{ -+ echo "${SEPGTEST}: [-u ] [-h ] [-p ] [-d ]" -+ exit 1 -+} -+ -+# parse options -+CMD_OPTS=`getopt -n "${SEPGTEST}" "u:h:p:d:" $*` || usage -+eval set -- $CMD_OPTS -+while [ -n "$1" ]; -+do -+ case $1 in -+ -u) -+ DBUSER="-U $2"; shift 2;; -+ -h) -+ DBHOST="-h $2"; shift 2;; -+ -p) -+ DBPORT="-p $2"; shift 2;; -+ -d) -+ DBNAME="$2"; shift 2;; -+ --) -+ shift; break;; -+ *) -+ usage; shift 2;; -+ esac -+done -+ -+PSQL_OPTS="${PSQL} ${DBUSER} ${DBHOST} ${DBPORT} ${DBNAME}" -+ -+# check environment -+ -+ -+ -diff --git a/git-update-branches.sh b/git-update-branches.sh -new file mode 100755 -index 0000000..2f25fde ---- /dev/null -+++ b/git-update-branches.sh -@@ -0,0 +1,89 @@ -+#!/bin/sh -+ -+GIT_BIN=`which git` -+GIT_DIR=`(cd \`dirname $0\`; pwd)` -+UPSTREAM_URL="git://git.postgresql.org/git/postgresql.git" -+UPSTREAM_BRANCH="master" -+LOCAL_BRANCHES="master v9.0/sepgsql" -+RPMSOURCE=`rpm -E '%{_sourcedir}'` -+#================================================================ -+ -+cd ${GIT_DIR} -+ -+CURRENT_BRANCH=`${GIT_BIN} branch -l | grep '^*' | awk '{print $2}'` -+ -+for tree in ${LOCAL_BRANCHES} -+do -+ ${GIT_BIN} branch -lr | grep -q "origin/${tree}" || exit 1 -+ ${GIT_BIN} branch -l | grep -q "${tree}" || \ -+ ${GIT_BIN} checkout --track "origin/${tree}" || exit 1 -+done -+ -+# ======== create patches ======== -+if [ "$1" = "--patch" ]; then -+ LEFT_TREE="" -+ RIGHT_TREE="" -+ NUM=0 -+ -+ BASE_VERSION=`grep AC_INIT ${GIT_DIR}/configure.in \ -+ | head -1 \ -+ | sed -e 's/,/ /g' -e 's/\[//g' -e 's/\]//g' \ -+ | awk '{print $2}'` -+ BASE_MAJOR=`echo $BASE_VERSION | sed 's/\.[0-9]\+$//g'` -+ -+ for tree in ${LOCAL_BRANCHES} -+ do -+ LEFT_TREE=$RIGHT_TREE -+ RIGHT_TREE=$tree -+ -+ if [ -n "$LEFT_TREE" -a -n "$RIGHT_TREE" ]; then -+ tag=`basename ${RIGHT_TREE}` -+ ${GIT_BIN} diff $LEFT_TREE $RIGHT_TREE \ -+ > $RPMSOURCE/pgsql-${NUM}-${BASE_MAJOR}-${tag}.patch -+ echo "${NUM}) $RPMSOURCE/pgsql-${NUM}-${BASE_MAJOR}-${tag}.patch" -+ fi -+ NUM=`printf "%02d" \`expr $NUM + 1\`` -+ done -+ -+ exit 0 -+fi -+ -+# ======== sync remote git ======== -+ -+if echo "$1" | grep -q '^--sync-upstream=\?'; then -+ UPSTREAM_TAG=`echo "$1" | sed 's/^--sync-upstream=\?//g'` -+ if [ -n "${UPSTREAM_TAG}" ]; then -+ UPSTREAM_TAG=HEAD -+ fi -+ -+ LOCAL_MASTER=`echo "${LOCAL_BRANCHES}" | awk '{print $1}'` -+ -+ ${GIT_BIN} checkout ${LOCAL_MASTER} || exit 1 -+ ${GIT_BIN} pull ${UPSTREAM_URL} ${UPSTREAM_BRANCH} ${UPSTREAM_TAG} -+ if [ $? -ne 0 ]; then -+ ${GIT_BIN} reset HEAD -+ ${GIT_BIN} checkout . -+ ${GIT_BIN} clean -fd -+ ${GIT_BIN} pull --no-commit ${UPSTREAM_URL} ${UPSTREAM_BRANCH} ${UPSTREAM_TAG} -+ exit 2 -+ fi -+fi -+ -+LEFT_TREE="" -+for RIGHT_TREE in ${LOCAL_BRANCHES} -+do -+ if [ -n "${LEFT_TREE}" -a -n "${RIGHT_TREE}" ]; then -+ ${GIT_BIN} checkout ${RIGHT_TREE} || exit 1 -+ ${GIT_BIN} pull ${GIT_DIR} ${LEFT_TREE} || exit 1 -+ if [ $? -ne 0 ]; then -+ ${GIT_BIN} reset HEAD -+ ${GIT_BIN} checkout . -+ ${GIT_BIN} clean -fd -+ ${GIT_BIN} pull --no-commit ${GIT_DIR} ${LEFT_TREE} -+ exit 2 -+ fi -+ fi -+ LEFT_TREE=${RIGHT_TREE} -+done -+ -+${GIT_BIN} checkout ${CURRENT_BRANCH} diff --git a/src/Makefile.global.in b/src/Makefile.global.in -index 1c38ac2..0e3bed5 100644 +index 280578a..6495097 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in -@@ -164,6 +164,7 @@ enable_nls = @enable_nls@ +@@ -166,6 +166,7 @@ enable_nls = @enable_nls@ enable_debug = @enable_debug@ enable_dtrace = @enable_dtrace@ enable_coverage = @enable_coverage@ @@ -552,10 +355,10 @@ index 1c38ac2..0e3bed5 100644 python_includespec = @python_includespec@ diff --git a/src/backend/Makefile b/src/backend/Makefile -index 218544e..0b1dd31 100644 +index a11b2b5..25198fd 100644 --- a/src/backend/Makefile +++ b/src/backend/Makefile -@@ -16,7 +16,7 @@ include $(top_builddir)/src/Makefile.global +@@ -19,7 +19,7 @@ include $(top_builddir)/src/Makefile.global SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ main nodes optimizer port postmaster regex replication rewrite \ @@ -564,7 +367,7 @@ index 218544e..0b1dd31 100644 include $(srcdir)/common.mk -@@ -40,6 +40,11 @@ LIBS := $(filter-out -lpgport, $(LIBS)) $(LDAP_LIBS_BE) +@@ -43,6 +43,11 @@ LIBS := $(filter-out -lpgport, $(LIBS)) $(LDAP_LIBS_BE) # The backend doesn't need everything that's in LIBS, however LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) @@ -577,7 +380,7 @@ index 218544e..0b1dd31 100644 all: submake-libpgport submake-schemapg postgres $(POSTGRES_IMP) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c -index 6ec73f0..db0023f 100644 +index 8d770a3..a3b1474 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -60,6 +60,7 @@ @@ -630,7 +433,7 @@ index 6ec73f0..db0023f 100644 if (tupleDescriptor->tdhasoid) len += sizeof(Oid); + if (tupleDescriptor->tdhassecid) -+ len += sizeof(Oid); ++ len += sizeof(Oid)+1; hoff = len = MAXALIGN(len); /* align user data safely */ @@ -644,7 +447,7 @@ index 6ec73f0..db0023f 100644 heap_fill_tuple(tupleDescriptor, values, diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c -index 9a8611f..325dbde 100644 +index 2125fdf..5880ec7 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -34,7 +34,7 @@ @@ -738,7 +541,7 @@ index 9a8611f..325dbde 100644 attnum = 0; diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c -index f01ed1e..4e8dfcb 100644 +index 2a75820..065ba1a 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -33,7 +33,7 @@ initGinState(GinState *state, Relation index) @@ -751,7 +554,7 @@ index f01ed1e..4e8dfcb 100644 TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL, INT2OID, -1, 0); diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c -index 759fc35..2fc282d 100644 +index 48a387e..f00a9cc 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -52,6 +52,7 @@ @@ -818,7 +621,7 @@ index 759fc35..2fc282d 100644 MarkBufferDirty(buffer); diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c -index 2af81df..18341f4 100644 +index 7518db1..fb78993 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, @@ -844,12 +647,12 @@ index 2af81df..18341f4 100644 if (olddata->t_infomask & HEAP_HASOID) new_len += sizeof(Oid); + if (HeapTupleHeaderHasSecid(olddata)) -+ new_len += sizeof(Oid); ++ new_len += sizeof(Oid)+1; new_len = MAXALIGN(new_len); Assert(new_len == olddata->t_hoff); new_data_len = heap_compute_data_size(tupleDesc, diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c -index d432c9d..28e7bde 100644 +index d6dca97..710b891 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -605,7 +605,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS) @@ -862,7 +665,7 @@ index d432c9d..28e7bde 100644 XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "gid", diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c -index b88cff2..d6636f7 100644 +index 8436dea..d9de0e4 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -36,6 +36,7 @@ @@ -891,7 +694,7 @@ index b88cff2..d6636f7 100644 false, /* entry-time xact r/o state */ false, /* startedInRecovery */ NULL /* link to parent state block */ -@@ -1658,6 +1663,10 @@ StartTransaction(void) +@@ -1698,6 +1703,10 @@ StartTransaction(void) /* SecurityRestrictionContext should never be set outside a transaction */ Assert(s->prevSecContext == 0); @@ -902,7 +705,7 @@ index b88cff2..d6636f7 100644 /* * initialize other subsystems for new transaction */ -@@ -2162,6 +2171,10 @@ AbortTransaction(void) +@@ -2202,6 +2211,10 @@ AbortTransaction(void) */ SetUserIdAndSecContext(s->prevUser, s->prevSecContext); @@ -913,7 +716,7 @@ index b88cff2..d6636f7 100644 /* * do abort processing */ -@@ -4006,6 +4019,10 @@ AbortSubTransaction(void) +@@ -4046,6 +4059,10 @@ AbortSubTransaction(void) */ SetUserIdAndSecContext(s->prevUser, s->prevSecContext); @@ -924,7 +727,7 @@ index b88cff2..d6636f7 100644 /* * We can skip all this stuff if the subxact failed before creating a * ResourceOwner... -@@ -4145,6 +4162,8 @@ PushTransaction(void) +@@ -4185,6 +4202,8 @@ PushTransaction(void) s->state = TRANS_DEFAULT; s->blockState = TBLOCK_SUBBEGIN; GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); @@ -934,10 +737,10 @@ index b88cff2..d6636f7 100644 CurrentTransactionState = s; diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c -index 0b05e25..4f783e5 100644 +index 15b3476..54a9105 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c -@@ -8765,7 +8765,7 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS) +@@ -8895,7 +8895,7 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS) * Construct a tuple descriptor for the result row. This must match this * function's pg_proc entry! */ @@ -947,7 +750,7 @@ index 0b05e25..4f783e5 100644 TEXTOID, -1, 0); TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "file_offset", diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y -index 387d43e..aecb449 100644 +index 9cc6850..7537984 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -32,6 +32,7 @@ @@ -982,7 +785,7 @@ index 387d43e..aecb449 100644 } do_end(); diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c -index 455eac1..0590156 100644 +index 080d80e..a870b31 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -33,6 +33,7 @@ @@ -1035,7 +838,7 @@ index 455eac1..0590156 100644 tuple = heap_form_tuple(tupDesc, values, Nulls); if (objectid != (Oid) 0) diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile -index dafae3f..c2aea7a 100644 +index a970039..0381d6b 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -13,7 +13,7 @@ include $(top_builddir)/src/Makefile.global @@ -1057,7 +860,7 @@ index dafae3f..c2aea7a 100644 pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ pg_default_acl.h \ diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c -index 2561d83..002a66c 100644 +index 0470a70..62fedb1 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -38,6 +38,7 @@ @@ -1074,9 +877,9 @@ index 2561d83..002a66c 100644 #include "parser/parse_func.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" + #include "utils/builtins.h" #include "utils/fmgroids.h" - #include "utils/lsyscache.h" -@@ -1499,6 +1501,10 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, +@@ -1500,6 +1502,10 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) continue; @@ -1087,7 +890,7 @@ index 2561d83..002a66c 100644 /* Views don't have any system columns at all */ if (classForm->relkind == RELKIND_VIEW && curr_att < 0) continue; -@@ -1608,6 +1614,8 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, +@@ -1609,6 +1615,8 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, relOid, grantorId, ACL_KIND_COLUMN, relname, attnum, NameStr(pg_attribute_tuple->attname)); @@ -1096,7 +899,7 @@ index 2561d83..002a66c 100644 /* * Generate new ACL. -@@ -1869,6 +1877,8 @@ ExecGrant_Relation(InternalGrant *istmt) +@@ -1870,6 +1878,8 @@ ExecGrant_Relation(InternalGrant *istmt) ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, NameStr(pg_class_tuple->relname), 0, NULL); @@ -1105,7 +908,7 @@ index 2561d83..002a66c 100644 /* * Generate new ACL. -@@ -2063,6 +2073,8 @@ ExecGrant_Database(InternalGrant *istmt) +@@ -2064,6 +2074,8 @@ ExecGrant_Database(InternalGrant *istmt) datId, grantorId, ACL_KIND_DATABASE, NameStr(pg_database_tuple->datname), 0, NULL); @@ -1114,7 +917,7 @@ index 2561d83..002a66c 100644 /* * Generate new ACL. -@@ -2188,6 +2200,8 @@ ExecGrant_Fdw(InternalGrant *istmt) +@@ -2189,6 +2201,8 @@ ExecGrant_Fdw(InternalGrant *istmt) fdwid, grantorId, ACL_KIND_FDW, NameStr(pg_fdw_tuple->fdwname), 0, NULL); @@ -1123,7 +926,7 @@ index 2561d83..002a66c 100644 /* * Generate new ACL. -@@ -2313,6 +2327,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) +@@ -2314,6 +2328,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) srvid, grantorId, ACL_KIND_FOREIGN_SERVER, NameStr(pg_server_tuple->srvname), 0, NULL); @@ -1132,7 +935,7 @@ index 2561d83..002a66c 100644 /* * Generate new ACL. -@@ -2831,6 +2847,8 @@ ExecGrant_Namespace(InternalGrant *istmt) +@@ -2832,6 +2848,8 @@ ExecGrant_Namespace(InternalGrant *istmt) nspid, grantorId, ACL_KIND_NAMESPACE, NameStr(pg_namespace_tuple->nspname), 0, NULL); @@ -1141,7 +944,7 @@ index 2561d83..002a66c 100644 /* * Generate new ACL. -@@ -2955,6 +2973,8 @@ ExecGrant_Tablespace(InternalGrant *istmt) +@@ -2956,6 +2974,8 @@ ExecGrant_Tablespace(InternalGrant *istmt) tblId, grantorId, ACL_KIND_TABLESPACE, NameStr(pg_tablespace_tuple->spcname), 0, NULL); @@ -1151,7 +954,7 @@ index 2561d83..002a66c 100644 /* * Generate new ACL. diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c -index 3edfc23..b451e59 100644 +index 1739085..36d4852 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -32,6 +32,7 @@ @@ -1189,7 +992,7 @@ index 3edfc23..b451e59 100644 relationId == PgShdescriptionToastIndex || relationId == PgDbRoleSettingToastTable || diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl -index 31aabda..c9fc09e 100644 +index 85d1d71..853074e 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl @@ -218,7 +218,8 @@ foreach my $catname ( @{ $catalogs->{names} } ) @@ -1203,7 +1006,7 @@ index 31aabda..c9fc09e 100644 foreach my $attr (@SYS_ATTRS) { diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c -index d848ef0..deaf2fb 100644 +index 47e0c9b..73cbfcf 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -43,6 +43,7 @@ @@ -1547,7 +1350,7 @@ index d848ef0..deaf2fb 100644 diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c -index 69946fe..23fb19b 100644 +index dea6889..18d96da 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -39,6 +39,7 @@ @@ -1656,7 +1459,7 @@ index 69946fe..23fb19b 100644 /* ---------------- * update pg_index diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c -index 5581346..cf1910a 100644 +index 2e5bb27..e730904 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -40,6 +40,7 @@ @@ -1677,7 +1480,7 @@ index 5581346..cf1910a 100644 return namespaceId; } -@@ -2903,7 +2907,8 @@ recomputeNamespacePath(void) +@@ -2911,7 +2915,8 @@ recomputeNamespacePath(void) if (OidIsValid(namespaceId) && !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, @@ -1687,7 +1490,7 @@ index 5581346..cf1910a 100644 oidlist = lappend_oid(oidlist, namespaceId); } } -@@ -2930,7 +2935,8 @@ recomputeNamespacePath(void) +@@ -2938,7 +2943,8 @@ recomputeNamespacePath(void) if (OidIsValid(namespaceId) && !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, @@ -1697,7 +1500,7 @@ index 5581346..cf1910a 100644 oidlist = lappend_oid(oidlist, namespaceId); } } -@@ -2996,9 +3002,12 @@ InitTempTableNamespace(void) +@@ -3004,9 +3010,12 @@ InitTempTableNamespace(void) char namespaceName[NAMEDATALEN]; Oid namespaceId; Oid toastspaceId; @@ -1710,7 +1513,7 @@ index 5581346..cf1910a 100644 /* * First, do permission check to see if we are authorized to make temp * tables. We use a nonstandard error message here since "databasename: -@@ -3016,6 +3025,9 @@ InitTempTableNamespace(void) +@@ -3024,6 +3033,9 @@ InitTempTableNamespace(void) errmsg("permission denied to create temporary tables in database \"%s\"", get_database_name(MyDatabaseId)))); @@ -1720,7 +1523,7 @@ index 5581346..cf1910a 100644 /* * Do not allow a Hot Standby slave session to make temp tables. Aside * from problems with modifying the system catalogs, there is a naming -@@ -3031,8 +3043,6 @@ InitTempTableNamespace(void) +@@ -3039,8 +3051,6 @@ InitTempTableNamespace(void) (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), errmsg("cannot create temporary tables during recovery"))); @@ -1729,7 +1532,7 @@ index 5581346..cf1910a 100644 namespaceId = GetSysCacheOid1(NAMESPACENAME, CStringGetDatum(namespaceName)); if (!OidIsValid(namespaceId)) -@@ -3045,7 +3055,9 @@ InitTempTableNamespace(void) +@@ -3053,7 +3063,9 @@ InitTempTableNamespace(void) * temp tables. This works because the places that access the temp * namespace for my own backend skip permissions checks on it. */ @@ -1740,7 +1543,7 @@ index 5581346..cf1910a 100644 /* Advance command counter to make namespace visible */ CommandCounterIncrement(); } -@@ -3070,7 +3082,9 @@ InitTempTableNamespace(void) +@@ -3078,7 +3090,9 @@ InitTempTableNamespace(void) CStringGetDatum(namespaceName)); if (!OidIsValid(toastspaceId)) { @@ -1752,7 +1555,7 @@ index 5581346..cf1910a 100644 CommandCounterIncrement(); } diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c -index 582d894..d7b6bd1 100644 +index 9672ecf..b28a314 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -27,6 +27,7 @@ @@ -1793,7 +1596,7 @@ index 582d894..d7b6bd1 100644 /* * Okay to create the pg_aggregate entry. diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c -index 99085c9..b4c0b3a 100644 +index 574eef5..2a46cdb 100644 --- a/src/backend/catalog/pg_conversion.c +++ b/src/backend/catalog/pg_conversion.c @@ -40,7 +40,7 @@ Oid @@ -1815,7 +1618,7 @@ index 99085c9..b4c0b3a 100644 oid = simple_heap_insert(rel, tup); Assert(OidIsValid(oid)); diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c -index e3f18bf..572ce01 100644 +index 7c5f56f..fb8bf22 100644 --- a/src/backend/catalog/pg_largeobject.c +++ b/src/backend/catalog/pg_largeobject.c @@ -21,10 +21,13 @@ @@ -1914,9 +1717,9 @@ index e3f18bf..572ce01 100644 +/* * LargeObjectExists * - * We don't use the system cache to for large object metadata, for fear of + * We don't use the system cache for large object metadata, for fear of diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c -index 22111a3..cba7274 100644 +index 79d03b3..4357a9e 100644 --- a/src/backend/catalog/pg_namespace.c +++ b/src/backend/catalog/pg_namespace.c @@ -28,7 +28,7 @@ @@ -1938,7 +1741,7 @@ index 22111a3..cba7274 100644 nspoid = simple_heap_insert(nspdesc, tup); Assert(OidIsValid(nspoid)); diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c -index 2362268..f6df55e 100644 +index 8eebb1d..71be3c5 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -28,6 +28,7 @@ @@ -2019,7 +1822,7 @@ index 2362268..f6df55e 100644 } diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c -index d76e415..f4c74a3 100644 +index f60cc61..a2ccc76 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -84,7 +84,8 @@ ProcedureCreate(const char *procedureName, @@ -2704,7 +2507,7 @@ index 0000000..4816635 + PG_RETURN_OID(HeapTupleHeaderGetSecid(htup)); +} diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c -index 76f9e06..0b4dcc3 100644 +index d4fdea9..a1220ea 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -25,6 +25,7 @@ @@ -2771,7 +2574,7 @@ index 76f9e06..0b4dcc3 100644 } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c -index 435dfdd..5d86354 100644 +index 86e7daa..82cf587 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -24,6 +24,7 @@ @@ -2822,7 +2625,7 @@ index 435dfdd..5d86354 100644 /* make the toast relation visible, else index creation will fail */ CommandCounterIncrement(); diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c -index 4964fb3..390a1c1 100644 +index 2c6ce59..312d59f 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -32,6 +32,7 @@ @@ -2844,10 +2647,10 @@ index 4964fb3..390a1c1 100644 namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname); simple_heap_update(rel, &tup->t_self, tup); diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c -index 17e1e77..c1c8bfd 100644 +index cecdfa5..5b51f68 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c -@@ -289,3 +289,64 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) +@@ -290,3 +290,64 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) (int) stmt->objectType); } } @@ -2913,7 +2716,7 @@ index 17e1e77..c1c8bfd 100644 + } +} diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c -index ccb4599..394740a 100644 +index 7a1b8e8..69988d0 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -30,12 +30,14 @@ @@ -3002,7 +2805,7 @@ index ccb4599..394740a 100644 /* diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c -index 64792f2..6d71642 100644 +index 7fa09c8..2deb171 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -49,6 +49,7 @@ @@ -3173,7 +2976,7 @@ index 64792f2..6d71642 100644 ReleaseSysCache(tuple); /* Call CreateComments() to create/drop the comments */ -@@ -1505,6 +1554,9 @@ CommentCast(List *qualname, List *arguments, char *comment) +@@ -1482,6 +1531,9 @@ CommentCast(List *qualname, List *arguments, char *comment) format_type_be(sourcetypeid), format_type_be(targettypeid)))); @@ -3183,7 +2986,7 @@ index 64792f2..6d71642 100644 ReleaseSysCache(tuple); /* Call CreateComments() to create/drop the comments */ -@@ -1522,6 +1574,8 @@ CommentTSParser(List *qualname, char *comment) +@@ -1499,6 +1551,8 @@ CommentTSParser(List *qualname, char *comment) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to comment on text search parser"))); @@ -3192,7 +2995,7 @@ index 64792f2..6d71642 100644 CreateComments(prsId, TSParserRelationId, 0, comment); } -@@ -1536,6 +1590,8 @@ CommentTSDictionary(List *qualname, char *comment) +@@ -1513,6 +1567,8 @@ CommentTSDictionary(List *qualname, char *comment) if (!pg_ts_dict_ownercheck(dictId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, NameListToString(qualname)); @@ -3201,7 +3004,7 @@ index 64792f2..6d71642 100644 CreateComments(dictId, TSDictionaryRelationId, 0, comment); } -@@ -1551,6 +1607,8 @@ CommentTSTemplate(List *qualname, char *comment) +@@ -1528,6 +1584,8 @@ CommentTSTemplate(List *qualname, char *comment) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to comment on text search template"))); @@ -3210,7 +3013,7 @@ index 64792f2..6d71642 100644 CreateComments(tmplId, TSTemplateRelationId, 0, comment); } -@@ -1565,6 +1623,8 @@ CommentTSConfiguration(List *qualname, char *comment) +@@ -1542,6 +1600,8 @@ CommentTSConfiguration(List *qualname, char *comment) if (!pg_ts_config_ownercheck(cfgId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, NameListToString(qualname)); @@ -3220,7 +3023,7 @@ index 64792f2..6d71642 100644 CreateComments(cfgId, TSConfigRelationId, 0, comment); } diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c -index 57ddab0..0c10a64 100644 +index 2e5a7df..9647622 100644 --- a/src/backend/commands/conversioncmds.c +++ b/src/backend/commands/conversioncmds.c @@ -24,6 +24,7 @@ @@ -3289,7 +3092,7 @@ index 57ddab0..0c10a64 100644 /* * 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 9d46e47..270eb74 100644 +index 4e95a83..a09eabe 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -22,7 +22,10 @@ @@ -3794,7 +3597,7 @@ index 9d46e47..270eb74 100644 /* diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c -index e7dac22..16360db 100644 +index 3d54324..3687922 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -35,6 +35,8 @@ @@ -3983,7 +3786,7 @@ index e7dac22..16360db 100644 /* * Helper functions diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c -index 0bda24a..334b123 100644 +index e8dba94..4c4fc36 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -257,7 +257,7 @@ ExplainResultDesc(ExplainStmt *stmt) @@ -3996,7 +3799,7 @@ index 0bda24a..334b123 100644 xml ? XMLOID : TEXTOID, -1, 0); return tupdesc; diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c -index 14356a2..d1f255e 100644 +index abbe731..b36c928 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -27,6 +27,7 @@ @@ -4160,7 +3963,7 @@ index 14356a2..d1f255e 100644 * Do the deletion */ diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c -index 9a584ed..f914280 100644 +index 26a3a52..e2b8683 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -43,6 +43,7 @@ @@ -4337,10 +4140,10 @@ index 9a584ed..f914280 100644 ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c -index 94657b8..7cb3634 100644 +index 780dbc2..f471123 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c -@@ -39,6 +39,7 @@ +@@ -40,6 +40,7 @@ #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parsetree.h" @@ -4348,7 +4151,7 @@ index 94657b8..7cb3634 100644 #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/procarray.h" -@@ -242,6 +243,10 @@ DefineIndex(RangeVar *heapRelation, +@@ -243,6 +244,10 @@ DefineIndex(RangeVar *heapRelation, get_tablespace_name(tablespaceId)); } @@ -4359,7 +4162,7 @@ index 94657b8..7cb3634 100644 /* * Force shared indexes into the pg_global tablespace. This is a bit of a * hack but seems simpler than marking them in the BKI commands. On the -@@ -363,7 +368,9 @@ DefineIndex(RangeVar *heapRelation, +@@ -364,7 +369,9 @@ DefineIndex(RangeVar *heapRelation, errmsg("primary keys cannot be expressions"))); /* System attributes are never null, so no problem */ @@ -4370,7 +4173,7 @@ index 94657b8..7cb3634 100644 continue; atttuple = SearchSysCacheAttName(relationId, key->name); -@@ -1572,6 +1579,9 @@ ReindexIndex(RangeVar *indexRelation) +@@ -1600,6 +1607,9 @@ ReindexIndex(RangeVar *indexRelation) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, indexRelation->relname); @@ -4380,7 +4183,7 @@ index 94657b8..7cb3634 100644 ReleaseSysCache(tuple); reindex_index(indOid, false); -@@ -1604,6 +1614,9 @@ ReindexTable(RangeVar *relation) +@@ -1632,6 +1642,9 @@ ReindexTable(RangeVar *relation) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, relation->relname); @@ -4390,7 +4193,7 @@ index 94657b8..7cb3634 100644 ReleaseSysCache(tuple); if (!reindex_relation(heapOid, true, false)) -@@ -1642,6 +1655,9 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user) +@@ -1670,6 +1683,9 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, databaseName); @@ -4401,7 +4204,7 @@ index 94657b8..7cb3634 100644 * Create a memory context that will survive forced transaction commits we * do below. Since it is a child of PortalContext, it will go away diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c -index 283947a..657ff95 100644 +index 34d657c..d3c69a3 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -20,6 +20,7 @@ @@ -4423,7 +4226,7 @@ index 283947a..657ff95 100644 * If requested, recurse to children. We use find_inheritance_children * not find_all_inheritors to avoid taking locks far in advance of diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c -index ac0270f..25b84fd 100644 +index cae1a31..c946c5c 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -35,6 +35,7 @@ @@ -4571,7 +4374,7 @@ index ac0270f..25b84fd 100644 /* rename */ namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); simple_heap_update(rel, &tup->t_self, tup); -@@ -1990,6 +2029,8 @@ AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) +@@ -2011,6 +2050,8 @@ AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } @@ -4580,7 +4383,7 @@ index ac0270f..25b84fd 100644 /* * Modify the owner --- okay to scribble on tup because it's a copy -@@ -2112,7 +2153,8 @@ AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) +@@ -2154,7 +2195,8 @@ AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } @@ -4591,7 +4394,7 @@ index ac0270f..25b84fd 100644 * Modify the owner --- okay to scribble on tup because it's a copy */ diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c -index 9c07cf3..2e19448 100644 +index fa84a9b..36a4ac7 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -45,6 +45,7 @@ @@ -4602,7 +4405,7 @@ index 9c07cf3..2e19448 100644 #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/rel.h" -@@ -319,6 +320,9 @@ RemoveOperator(RemoveFuncStmt *stmt) +@@ -329,6 +330,9 @@ RemoveOperator(RemoveFuncStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, NameListToString(operatorName)); @@ -4612,7 +4415,7 @@ index 9c07cf3..2e19448 100644 ReleaseSysCache(tup); /* -@@ -426,6 +430,8 @@ AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) +@@ -436,6 +440,8 @@ AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(oprForm->oprnamespace)); } @@ -4622,7 +4425,7 @@ index 9c07cf3..2e19448 100644 /* * Modify the owner --- okay to scribble on tup because it's a copy diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c -index e765382..a54dd3f 100644 +index 7fa8278..9b3662d 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -759,7 +759,7 @@ pg_prepared_statement(PG_FUNCTION_ARGS) @@ -4635,7 +4438,7 @@ index e765382..a54dd3f 100644 TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c -index 633a093..a3375b2 100644 +index 8292ae1..0ce601a 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -29,6 +29,7 @@ @@ -4677,7 +4480,7 @@ index 633a093..a3375b2 100644 } else diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c -index b0a9a22..57951fe 100644 +index b30fdce..8430e3e 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -21,10 +21,12 @@ @@ -4795,7 +4598,7 @@ index b0a9a22..57951fe 100644 + heap_close(rel, RowExclusiveLock); +} diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c -index f52e1d8..ecff05d 100644 +index 292a427..3c8a36f 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -26,6 +26,7 @@ @@ -4857,7 +4660,7 @@ index f52e1d8..ecff05d 100644 if (!seqrel->rd_islocaltemp) PreventCommandIfReadOnly("setval()"); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c -index 9b5ce65..34ffbe2 100644 +index c4d81bb..9516700 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -32,6 +32,7 @@ @@ -4906,7 +4709,7 @@ index 9b5ce65..34ffbe2 100644 static void ATExecDropNotNull(Relation rel, const char *colName); static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, const char *colName); -@@ -355,6 +360,7 @@ DefineRelation(CreateStmt *stmt, char relkind) +@@ -367,6 +372,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) List *old_constraints; bool localHasOids; int parentOidCount; @@ -4914,7 +4717,7 @@ index 9b5ce65..34ffbe2 100644 List *rawDefaults; List *cookedDefaults; Datum reloptions; -@@ -362,6 +368,7 @@ DefineRelation(CreateStmt *stmt, char relkind) +@@ -374,6 +380,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) AttrNumber attnum; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; Oid ofTypeId; @@ -4922,7 +4725,7 @@ index 9b5ce65..34ffbe2 100644 /* * Truncate relname to appropriate length (probably a waste of time, as -@@ -461,7 +468,8 @@ DefineRelation(CreateStmt *stmt, char relkind) +@@ -477,7 +484,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) */ schema = MergeAttributes(schema, stmt->inhRelations, stmt->relation->istemp, @@ -4932,7 +4735,7 @@ index 9b5ce65..34ffbe2 100644 /* * Create a tuple descriptor from the relation schema. Note that this -@@ -473,6 +481,16 @@ DefineRelation(CreateStmt *stmt, char relkind) +@@ -489,6 +497,16 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) localHasOids = interpretOidsOption(stmt->options); descriptor->tdhasoid = (localHasOids || parentOidCount > 0); @@ -4949,7 +4752,7 @@ index 9b5ce65..34ffbe2 100644 /* * Find columns with default values and prepare for insertion of the * defaults. Pre-cooked (that is, inherited) defaults go into a list of -@@ -546,7 +564,8 @@ DefineRelation(CreateStmt *stmt, char relkind) +@@ -562,7 +580,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) stmt->oncommit, reloptions, true, @@ -4959,7 +4762,7 @@ index 9b5ce65..34ffbe2 100644 StoreCatalogInheritance(relationId, inheritOids); -@@ -755,6 +774,9 @@ RemoveRelations(DropStmt *drop) +@@ -771,6 +790,9 @@ RemoveRelations(DropStmt *drop) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, rel->relname); @@ -4969,7 +4772,7 @@ index 9b5ce65..34ffbe2 100644 if (!allowSystemTableMods && IsSystemClass(classform)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -@@ -918,6 +940,9 @@ ExecuteTruncate(TruncateStmt *stmt) +@@ -934,6 +956,9 @@ ExecuteTruncate(TruncateStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(seq_rel)); @@ -4979,7 +4782,7 @@ index 9b5ce65..34ffbe2 100644 seq_relids = lappend_oid(seq_relids, seq_relid); relation_close(seq_rel, NoLock); -@@ -1086,6 +1111,8 @@ truncate_check_rel(Relation rel) +@@ -1102,6 +1127,8 @@ truncate_check_rel(Relation rel) if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); @@ -4988,7 +4791,7 @@ index 9b5ce65..34ffbe2 100644 if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, -@@ -1146,6 +1173,7 @@ storage_name(char c) +@@ -1162,6 +1189,7 @@ storage_name(char c) * 'supconstr' receives a list of constraints belonging to the parents, * updated as necessary to be valid for the child. * 'supOidCount' is set to the number of parents that have OID columns. @@ -4996,7 +4799,7 @@ index 9b5ce65..34ffbe2 100644 * * Return value: * Completed schema list. -@@ -1191,13 +1219,15 @@ storage_name(char c) +@@ -1207,13 +1235,15 @@ storage_name(char c) */ static List * MergeAttributes(List *schema, List *supers, bool istemp, @@ -5013,7 +4816,7 @@ index 9b5ce65..34ffbe2 100644 bool have_bogus_defaults = false; int child_attno; static Node bogus_marker = {0}; /* marks conflicting defaults */ -@@ -1325,6 +1355,8 @@ MergeAttributes(List *schema, List *supers, bool istemp, +@@ -1341,6 +1371,8 @@ MergeAttributes(List *schema, List *supers, bool istemp, if (relation->rd_rel->relhasoids) parentsWithOids++; @@ -5022,7 +4825,7 @@ index 9b5ce65..34ffbe2 100644 tupleDesc = RelationGetDescr(relation); constr = tupleDesc->constr; -@@ -1626,6 +1658,7 @@ MergeAttributes(List *schema, List *supers, bool istemp, +@@ -1642,6 +1674,7 @@ MergeAttributes(List *schema, List *supers, bool istemp, *supOids = parentOids; *supconstr = constraints; *supOidCount = parentsWithOids; @@ -5030,7 +4833,7 @@ index 9b5ce65..34ffbe2 100644 return schema; } -@@ -1985,6 +2018,9 @@ renameatt(Oid myrelid, +@@ -2001,6 +2034,9 @@ renameatt(Oid myrelid, errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(targetrelation)))); @@ -5040,7 +4843,7 @@ index 9b5ce65..34ffbe2 100644 /* * if the 'recurse' flag is set then we are supposed to rename this * attribute in all classes that inherit from 'relname' (as well as in -@@ -2136,6 +2172,9 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype) +@@ -2152,6 +2188,9 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype) errmsg("\"%s\" is not a view", RelationGetRelationName(targetrelation)))); @@ -5050,7 +4853,7 @@ index 9b5ce65..34ffbe2 100644 /* * Don't allow ALTER TABLE on composite types. We want people to use ALTER * TYPE for that. -@@ -2566,6 +2605,27 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, +@@ -2580,6 +2619,27 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, } pass = AT_PASS_DROP; break; @@ -5078,7 +4881,7 @@ index 9b5ce65..34ffbe2 100644 case AT_SetTableSpace: /* SET TABLESPACE */ ATSimplePermissionsRelationOrIndex(rel); /* This command never recurses */ -@@ -2690,7 +2750,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, +@@ -2709,7 +2769,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, case AT_AddColumn: /* ADD COLUMN */ case AT_AddColumnToView: /* add column via CREATE OR REPLACE * VIEW */ @@ -5087,7 +4890,7 @@ index 9b5ce65..34ffbe2 100644 break; case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */ ATExecColumnDefault(rel, cmd->name, cmd->def); -@@ -2762,17 +2822,22 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, +@@ -2781,17 +2841,22 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, case AT_AddOids: /* SET WITH OIDS */ /* Use the ADD COLUMN code, unless prep decided to do nothing */ if (cmd->def != NULL) @@ -5113,7 +4916,7 @@ index 9b5ce65..34ffbe2 100644 /* * Nothing to do here; Phase 3 does the work */ -@@ -3126,6 +3191,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) +@@ -3145,6 +3210,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) MemoryContext oldCxt; List *dropped_attrs = NIL; ListCell *lc; @@ -5121,7 +4924,7 @@ index 9b5ce65..34ffbe2 100644 econtext = GetPerTupleExprContext(estate); -@@ -3156,6 +3222,19 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) +@@ -3175,6 +3241,19 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) } /* @@ -5141,7 +4944,7 @@ index 9b5ce65..34ffbe2 100644 * Scan through the rows, generating a new row if needed and then * checking all the constraints. */ -@@ -3172,11 +3251,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) +@@ -3191,11 +3270,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) if (newrel) { Oid tupOid = InvalidOid; @@ -5158,7 +4961,7 @@ index 9b5ce65..34ffbe2 100644 /* Set dropped attributes to null in new tuple */ foreach(lc, dropped_attrs) -@@ -3208,6 +3292,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) +@@ -3227,6 +3311,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) /* Preserve OID, if any */ if (newTupDesc->tdhasoid) HeapTupleSetOid(tuple, tupOid); @@ -5168,7 +4971,7 @@ index 9b5ce65..34ffbe2 100644 } /* Now check any constraints on the possibly-changed tuple */ -@@ -3601,7 +3688,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, +@@ -3625,7 +3712,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, @@ -5177,24 +4980,18 @@ index 9b5ce65..34ffbe2 100644 { Oid myrelid = RelationGetRelid(rel); Relation pgclass, -@@ -3615,12 +3702,16 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3639,6 +3726,10 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, int32 typmod; Form_pg_type tform; Expr *defval; + Oid securityId; - - if (rel->rd_rel->reloftype) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot add column to typed table"))); - ++ + /* SELinux permission check */ + securityId = sepgsql_attribute_create(myrelid, colDef->colname); -+ + attrdesc = heap_open(AttributeRelationId, RowExclusiveLock); - /* -@@ -3648,6 +3739,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3667,6 +3758,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, errmsg("child table \"%s\" has different type for column \"%s\"", RelationGetRelationName(rel), colDef->colname))); @@ -5208,7 +5005,7 @@ index 9b5ce65..34ffbe2 100644 /* If it's OID, child column must actually be OID */ if (isOid && childatt->attnum != ObjectIdAttributeNumber) ereport(ERROR, -@@ -3655,6 +3753,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3674,6 +3772,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, errmsg("child table \"%s\" has a conflicting \"%s\" column", RelationGetRelationName(rel), colDef->colname))); @@ -5222,7 +5019,7 @@ index 9b5ce65..34ffbe2 100644 /* Bump the existing child att's inhcount */ childatt->attinhcount++; simple_heap_update(attrdesc, &tuple->t_self, tuple); -@@ -3694,6 +3799,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3713,6 +3818,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, /* Determine the new attribute's number */ if (isOid) newattnum = ObjectIdAttributeNumber; @@ -5231,7 +5028,7 @@ index 9b5ce65..34ffbe2 100644 else { newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1; -@@ -3733,7 +3840,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3752,7 +3859,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ReleaseSysCache(typeTuple); @@ -5240,7 +5037,7 @@ index 9b5ce65..34ffbe2 100644 heap_close(attrdesc, RowExclusiveLock); -@@ -3742,6 +3849,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3761,6 +3868,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, */ if (isOid) ((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true; @@ -5249,7 +5046,7 @@ index 9b5ce65..34ffbe2 100644 else ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum; -@@ -3853,7 +3962,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3872,7 +3981,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, * If we are adding an OID column, we have to tell Phase 3 to rewrite the * table to fix that. */ @@ -5258,7 +5055,7 @@ index 9b5ce65..34ffbe2 100644 tab->new_changeoids = true; /* -@@ -3906,6 +4015,31 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) +@@ -3925,6 +4034,31 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) } /* @@ -5290,7 +5087,7 @@ index 9b5ce65..34ffbe2 100644 * ALTER TABLE ALTER COLUMN DROP NOT NULL */ static void -@@ -3917,6 +4051,9 @@ ATExecDropNotNull(Relation rel, const char *colName) +@@ -3936,6 +4070,9 @@ ATExecDropNotNull(Relation rel, const char *colName) List *indexoidlist; ListCell *indexoidscan; @@ -5300,7 +5097,7 @@ index 9b5ce65..34ffbe2 100644 /* * lookup the attribute */ -@@ -4007,6 +4144,9 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, +@@ -4026,6 +4163,9 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, AttrNumber attnum; Relation attr_rel; @@ -5310,7 +5107,7 @@ index 9b5ce65..34ffbe2 100644 /* * lookup the attribute */ -@@ -4057,6 +4197,9 @@ ATExecColumnDefault(Relation rel, const char *colName, +@@ -4076,6 +4216,9 @@ ATExecColumnDefault(Relation rel, const char *colName, { AttrNumber attnum; @@ -5320,7 +5117,7 @@ index 9b5ce65..34ffbe2 100644 /* * get the number of the attribute */ -@@ -4131,6 +4274,9 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue) +@@ -4150,6 +4293,9 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue) HeapTuple tuple; Form_pg_attribute attrtuple; @@ -5330,7 +5127,7 @@ index 9b5ce65..34ffbe2 100644 Assert(IsA(newValue, Integer)); newtarget = intVal(newValue); -@@ -4197,6 +4343,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, +@@ -4216,6 +4362,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, bool repl_null[Natts_pg_attribute]; bool repl_repl[Natts_pg_attribute]; @@ -5340,7 +5137,7 @@ index 9b5ce65..34ffbe2 100644 attrelation = heap_open(AttributeRelationId, RowExclusiveLock); tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName); -@@ -4256,6 +4405,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue) +@@ -4275,6 +4424,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue) HeapTuple tuple; Form_pg_attribute attrtuple; @@ -5350,7 +5147,7 @@ index 9b5ce65..34ffbe2 100644 Assert(IsA(newValue, String)); storagemode = strVal(newValue); -@@ -4346,6 +4498,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, +@@ -4373,6 +4525,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, if (recursing) ATSimplePermissions(rel, false); @@ -5360,7 +5157,7 @@ index 9b5ce65..34ffbe2 100644 /* * get the number of the attribute */ -@@ -4371,8 +4526,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, +@@ -4398,8 +4553,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, attnum = targetatt->attnum; @@ -5373,7 +5170,7 @@ index 9b5ce65..34ffbe2 100644 ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot drop system column \"%s\"", -@@ -4488,7 +4645,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, +@@ -4515,7 +4672,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, * If we dropped the OID column, must adjust pg_class.relhasoids and tell * Phase 3 to physically get rid of the column. */ @@ -5383,7 +5180,7 @@ index 9b5ce65..34ffbe2 100644 { Relation class_rel; Form_pg_class tuple_class; -@@ -4503,7 +4661,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, +@@ -4530,7 +4688,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, RelationGetRelid(rel)); tuple_class = (Form_pg_class) GETSTRUCT(tuple); @@ -5396,7 +5193,7 @@ index 9b5ce65..34ffbe2 100644 simple_heap_update(class_rel, &tuple->t_self, tuple); /* Keep the catalog indexes up to date */ -@@ -4650,6 +4812,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, +@@ -4677,6 +4839,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, if (recursing) ATSimplePermissions(rel, false); @@ -5406,7 +5203,7 @@ index 9b5ce65..34ffbe2 100644 /* * Call AddRelationNewConstraints to do the work, making sure it works on * a copy of the Constraint so transformExpr can't modify the original. It -@@ -4847,6 +5012,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, +@@ -4883,6 +5048,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, checkFkeyPermissions(pkrel, pkattnum, numpks); checkFkeyPermissions(rel, fkattnum, numfks); @@ -5416,7 +5213,7 @@ index 9b5ce65..34ffbe2 100644 /* * Look up the equality operators to use in the constraint. * -@@ -5592,6 +5760,9 @@ ATExecDropConstraint(Relation rel, const char *constrName, +@@ -5628,6 +5796,9 @@ ATExecDropConstraint(Relation rel, const char *constrName, if (recursing) ATSimplePermissions(rel, false); @@ -5426,7 +5223,7 @@ index 9b5ce65..34ffbe2 100644 conrel = heap_open(ConstraintRelationId, RowExclusiveLock); /* -@@ -5924,6 +6095,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, +@@ -5965,6 +6136,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, SysScanDesc scan; HeapTuple depTup; @@ -5436,7 +5233,7 @@ index 9b5ce65..34ffbe2 100644 attrelation = heap_open(AttributeRelationId, RowExclusiveLock); /* Look up the target column */ -@@ -6537,6 +6711,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) +@@ -6578,6 +6752,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } @@ -5445,7 +5242,7 @@ index 9b5ce65..34ffbe2 100644 } memset(repl_null, false, sizeof(repl_null)); -@@ -6702,6 +6878,9 @@ ATExecClusterOn(Relation rel, const char *indexName) +@@ -6743,6 +6919,9 @@ ATExecClusterOn(Relation rel, const char *indexName) { Oid indexOid; @@ -5455,7 +5252,7 @@ index 9b5ce65..34ffbe2 100644 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace); if (!OidIsValid(indexOid)) -@@ -6726,6 +6905,9 @@ ATExecClusterOn(Relation rel, const char *indexName) +@@ -6767,6 +6946,9 @@ ATExecClusterOn(Relation rel, const char *indexName) static void ATExecDropCluster(Relation rel) { @@ -5465,7 +5262,7 @@ index 9b5ce65..34ffbe2 100644 mark_index_clustered(rel, InvalidOid); } -@@ -6776,6 +6958,9 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) +@@ -6817,6 +6999,9 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) bool repl_repl[Natts_pg_class]; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; @@ -5475,7 +5272,7 @@ index 9b5ce65..34ffbe2 100644 if (defList == NIL) return; /* nothing to do */ -@@ -7090,6 +7275,9 @@ static void +@@ -7145,6 +7330,9 @@ static void ATExecEnableDisableTrigger(Relation rel, char *trigname, char fires_when, bool skip_system) { @@ -5485,7 +5282,7 @@ index 9b5ce65..34ffbe2 100644 EnableDisableTrigger(rel, trigname, fires_when, skip_system); } -@@ -7102,6 +7290,9 @@ static void +@@ -7157,6 +7345,9 @@ static void ATExecEnableDisableRule(Relation rel, char *trigname, char fires_when) { @@ -5495,7 +5292,7 @@ index 9b5ce65..34ffbe2 100644 EnableDisableRule(rel, trigname, fires_when); } -@@ -7135,6 +7326,10 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent) +@@ -7199,6 +7390,10 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent) */ ATSimplePermissions(parent_rel, false); @@ -5506,7 +5303,7 @@ index 9b5ce65..34ffbe2 100644 /* Permanent rels cannot inherit from temporary ones */ if (parent_rel->rd_istemp && !child_rel->rd_istemp) ereport(ERROR, -@@ -7487,6 +7682,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent) +@@ -7551,6 +7746,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent) List *connames; bool found = false; @@ -5516,7 +5313,7 @@ index 9b5ce65..34ffbe2 100644 /* * AccessShareLock on the parent is probably enough, seeing that DROP * TABLE doesn't lock parent tables at all. We need some lock since we'll -@@ -7784,6 +7982,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, +@@ -7848,6 +8046,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, /* get schema OID and check its permissions */ nspOid = LookupCreationNamespace(newschema); @@ -5526,7 +5323,7 @@ index 9b5ce65..34ffbe2 100644 if (oldNspOid == nspOid) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE), -@@ -7981,6 +8182,282 @@ AlterSeqNamespaces(Relation classRel, Relation rel, +@@ -8045,6 +8246,282 @@ AlterSeqNamespaces(Relation classRel, Relation rel, relation_close(depRel, AccessShareLock); } @@ -5810,7 +5607,7 @@ index 9b5ce65..34ffbe2 100644 /* * This code supports diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c -index 862cd2d..8ea4118 100644 +index 4c57a73..ce0bb99 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -65,6 +65,7 @@ @@ -5858,7 +5655,7 @@ index 862cd2d..8ea4118 100644 /* Disallow drop of the standard tablespaces, even by superuser */ if (tablespaceoid == GLOBALTABLESPACE_OID || tablespaceoid == DEFAULTTABLESPACE_OID) -@@ -787,6 +797,9 @@ RenameTableSpace(const char *oldname, const char *newname) +@@ -817,6 +827,9 @@ RenameTableSpace(const char *oldname, const char *newname) if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId())) aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, oldname); @@ -5868,7 +5665,7 @@ index 862cd2d..8ea4118 100644 /* Validate new name */ if (!allowSystemTableMods && IsReservedName(newname)) ereport(ERROR, -@@ -868,6 +881,9 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId) +@@ -898,6 +911,9 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId) /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); @@ -5878,7 +5675,7 @@ index 862cd2d..8ea4118 100644 /* * Normally we would also check for create permissions here, but there * are none for tablespaces so we follow what rename tablespace does -@@ -985,6 +1001,54 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt) +@@ -1015,6 +1031,54 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt) } /* @@ -5934,7 +5731,7 @@ index 862cd2d..8ea4118 100644 */ diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c -index 2cbc192..2ea9e6e 100644 +index 251c3e8..2f7d3d5 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -40,6 +40,7 @@ @@ -5977,7 +5774,7 @@ index 2cbc192..2ea9e6e 100644 * Grab an exclusive lock on the target table, which we will NOT release * until end of transaction. diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c -index ba3de63..19bebb4 100644 +index 522ea6e..8c6e0d3 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -35,6 +35,7 @@ @@ -6222,7 +6019,7 @@ index ba3de63..19bebb4 100644 /* Add or drop mappings */ diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c -index 1e14dca..f7429f0 100644 +index abde562..a5cf445 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -56,6 +56,7 @@ @@ -6529,7 +6326,7 @@ index 1e14dca..f7429f0 100644 + ReleaseSysCache(typtup); +} diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c -index 2f0788e..8de31ae 100644 +index cdf7dc5..f6aee43 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -21,11 +21,13 @@ @@ -6621,24 +6418,21 @@ index 2f0788e..8de31ae 100644 pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock); pg_authmem_dsc = RelationGetDescr(pg_authmem_rel); diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c -index 49a206e..4646c41 100644 +index e77430e..3dfc7d4 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c -@@ -980,6 +980,12 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound, - relation_close(onerel, NoLock); - - /* -+ * VACUUM FULL also reclaim orphan security labels, if exist -+ */ -+ if (vacstmt->options & VACOPT_FULL) +@@ -957,6 +957,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound, + */ + if (vacstmt->options & VACOPT_FULL) + { ++ /* Also reclaim unreferenced security labels */ + seclabelRelationReclaim(relid); + -+ /* - * Complete the transaction and free all temporary memory used. - */ - PopActiveSnapshot(); + /* close relation before vacuuming, but hold lock until commit */ + relation_close(onerel, NoLock); + onerel = NULL; diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c -index d7a06bc..5480c83 100644 +index c291529..dd0b12a 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -28,6 +28,7 @@ @@ -6660,7 +6454,7 @@ index d7a06bc..5480c83 100644 CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW"); diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c -index 5e555ad..963096d 100644 +index 2fa7e68..c2b926c 100644 --- a/src/backend/executor/execJunk.c +++ b/src/backend/executor/execJunk.c @@ -58,7 +58,8 @@ @@ -6683,7 +6477,7 @@ index 5e555ad..963096d 100644 /* * 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 d299310..febb02e 100644 +index 0b6cbcc..fbce8e4 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -38,6 +38,7 @@ @@ -6818,7 +6612,7 @@ index d299310..febb02e 100644 heap_insert(myState->rel, tuple, diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c -index 005e15e..6e93932 100644 +index bfcce0f..a07b314 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -49,6 +49,7 @@ @@ -6857,7 +6651,7 @@ index 005e15e..6e93932 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "column", -@@ -4108,6 +4112,9 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, +@@ -4118,6 +4122,9 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(acoerce->elemfuncid)); @@ -6868,7 +6662,7 @@ index 005e15e..6e93932 100644 fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), econtext->ecxt_per_query_memory); diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c -index 53fe195..68f790f 100644 +index fa5ff2d..fb9093c 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -20,6 +20,7 @@ @@ -6939,7 +6733,7 @@ index 53fe195..68f790f 100644 return true; } diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c -index 1caf976..64f6468 100644 +index e2ee706..d4d216b 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -98,7 +98,7 @@ @@ -7000,7 +6794,7 @@ index 1caf976..64f6468 100644 foreach(l, exprList) { diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c -index 98e4a64..14f9404 100644 +index 5886c1b..50f91f5 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -444,6 +444,7 @@ void @@ -7029,7 +6823,7 @@ index 98e4a64..14f9404 100644 } diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c -index d552698..ed1913f 100644 +index ea2cfc9..7b489db 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -1153,7 +1153,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, @@ -7060,7 +6854,7 @@ index d552698..ed1913f 100644 } Assert(tupdesc); diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c -index ddd91fc..cfb685e 100644 +index 74fc87a..4b222b1 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -89,6 +89,7 @@ @@ -7090,7 +6884,7 @@ index ddd91fc..cfb685e 100644 /* Create slot we're going to do argument evaluation in */ peraggstate->evalslot = ExecInitExtraTupleSlot(estate); diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c -index 66e6b74..a31907c 100644 +index 6989961..471653f 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -178,7 +178,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) @@ -7103,7 +6897,7 @@ index 66e6b74..a31907c 100644 (AttrNumber) 1, attname, diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c -index ca6b0f8..b5c79d0 100644 +index 948f580..7d9f4f4 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -98,6 +98,7 @@ @@ -7114,7 +6908,7 @@ index ca6b0f8..b5c79d0 100644 #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/memutils.h" -@@ -216,6 +217,9 @@ MJExamineQuals(List *mergeclauses, +@@ -224,6 +225,9 @@ MJExamineQuals(List *mergeclauses, aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(cmpproc)); @@ -7125,7 +6919,7 @@ index ca6b0f8..b5c79d0 100644 fmgr_info(cmpproc, &(clause->cmpfinfo)); diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c -index 7856b66..eee2739 100644 +index 9699381..03afb4e 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -38,11 +38,13 @@ @@ -7252,10 +7046,10 @@ index 7856b66..eee2739 100644 { + Oid securityId = InvalidOid; + - planSlot = ExecProcNode(subplanstate); - - if (TupIsNull(planSlot)) -@@ -705,6 +760,14 @@ ExecModifyTable(ModifyTableState *node) + /* + * Reset the per-output-tuple exprcontext. This is needed because + * triggers expect to use that context as workspace. It's a bit ugly +@@ -713,6 +768,14 @@ ExecModifyTable(ModifyTableState *node) if (junkfilter != NULL) { @@ -7270,7 +7064,7 @@ index 7856b66..eee2739 100644 /* * extract the 'ctid' junk attribute. */ -@@ -734,11 +797,11 @@ ExecModifyTable(ModifyTableState *node) +@@ -742,11 +805,11 @@ ExecModifyTable(ModifyTableState *node) switch (operation) { case CMD_INSERT: @@ -7284,7 +7078,7 @@ index 7856b66..eee2739 100644 break; case CMD_DELETE: slot = ExecDelete(tupleid, planSlot, -@@ -850,7 +913,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) +@@ -858,7 +921,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * RETURNING list. We assume the rest will look the same. */ tupDesc = ExecTypeFromTL((List *) linitial(node->returningLists), @@ -7293,7 +7087,7 @@ index 7856b66..eee2739 100644 /* Set up a slot for the output of the RETURNING projection(s) */ ExecInitResultTupleSlot(estate, &mtstate->ps); -@@ -884,7 +947,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) +@@ -892,7 +955,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * We still must construct a dummy result tuple type, because InitPlan * expects one (maybe should change that?). */ @@ -7302,7 +7096,7 @@ index 7856b66..eee2739 100644 ExecInitResultTupleSlot(estate, &mtstate->ps); ExecAssignResultType(&mtstate->ps, tupDesc); -@@ -976,6 +1039,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) +@@ -984,6 +1047,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) j = ExecInitJunkFilter(subplan->targetlist, resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, @@ -7311,7 +7105,7 @@ index 7856b66..eee2739 100644 if (operation == CMD_UPDATE || operation == CMD_DELETE) diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c -index 9f1ff16..bef704b 100644 +index d397b13..90e2e65 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -859,7 +859,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) @@ -7333,7 +7127,7 @@ index 9f1ff16..bef704b 100644 ExecSetSlotDescriptor(slot, tupDesc); sstate->projRight = ExecBuildProjectionInfo(righttlist, diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c -index 712bab1..e69f3c7 100644 +index de6d038..2ee0a8f 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -43,6 +43,7 @@ @@ -7358,7 +7152,7 @@ index 712bab1..e69f3c7 100644 perfuncstate->wfuncstate = wfuncstate; perfuncstate->wfunc = wfunc; diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c -index 7f0b5e4..2e2c63b 100644 +index 1ffb1b2..623b2dd 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -767,6 +767,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, @@ -7417,7 +7211,7 @@ index 7f0b5e4..2e2c63b 100644 char * diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c -index f4f50f8..4ab5698 100644 +index 464183d..67d0316 100644 --- a/src/backend/libpq/be-fsstubs.c +++ b/src/backend/libpq/be-fsstubs.c @@ -46,6 +46,7 @@ @@ -7580,10 +7374,10 @@ index f4f50f8..4ab5698 100644 PG_RETURN_INT32(0); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c -index e770e89..9642a66 100644 +index 3e237c1..387d85d 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c -@@ -1823,6 +1823,7 @@ _copyRangeTblEntry(RangeTblEntry *from) +@@ -1824,6 +1824,7 @@ _copyRangeTblEntry(RangeTblEntry *from) COPY_SCALAR_FIELD(checkAsUser); COPY_BITMAPSET_FIELD(selectedCols); COPY_BITMAPSET_FIELD(modifiedCols); @@ -7591,7 +7385,7 @@ index e770e89..9642a66 100644 return newnode; } -@@ -2755,6 +2756,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) +@@ -2756,6 +2757,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) return newnode; } @@ -7613,7 +7407,7 @@ index e770e89..9642a66 100644 static RuleStmt * _copyRuleStmt(RuleStmt *from) { -@@ -3968,6 +3984,9 @@ copyObject(void *from) +@@ -3970,6 +3986,9 @@ copyObject(void *from) case T_AlterOwnerStmt: retval = _copyAlterOwnerStmt(from); break; @@ -7624,7 +7418,7 @@ index e770e89..9642a66 100644 retval = _copyRuleStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c -index 5d83727..fd4071d 100644 +index dc5f883..33f2f5d 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1308,6 +1308,19 @@ _equalAlterOwnerStmt(AlterOwnerStmt *a, AlterOwnerStmt *b) @@ -7647,7 +7441,7 @@ index 5d83727..fd4071d 100644 _equalRuleStmt(RuleStmt *a, RuleStmt *b) { COMPARE_NODE_FIELD(relation); -@@ -2186,6 +2199,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) +@@ -2187,6 +2200,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(modifiedCols); @@ -7655,7 +7449,7 @@ index 5d83727..fd4071d 100644 return true; } -@@ -2657,6 +2671,9 @@ equal(void *a, void *b) +@@ -2658,6 +2672,9 @@ equal(void *a, void *b) case T_AlterOwnerStmt: retval = _equalAlterOwnerStmt(a, b); break; @@ -7666,10 +7460,10 @@ index 5d83727..fd4071d 100644 retval = _equalRuleStmt(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c -index e7dae4b..4e8b350 100644 +index 48c0888..34632d2 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c -@@ -2135,6 +2135,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) +@@ -2136,6 +2136,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) WRITE_OID_FIELD(checkAsUser); WRITE_BITMAPSET_FIELD(selectedCols); WRITE_BITMAPSET_FIELD(modifiedCols); @@ -7678,7 +7472,7 @@ index e7dae4b..4e8b350 100644 static void diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c -index bc6e2a6..792b9ff 100644 +index f28191d..f6f31e5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1171,6 +1171,7 @@ _readRangeTblEntry(void) @@ -7690,7 +7484,7 @@ index bc6e2a6..792b9ff 100644 READ_DONE(); } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c -index 3950ab4..18e5d80 100644 +index 317a7e8..5b88ad6 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -39,6 +39,7 @@ @@ -7719,7 +7513,7 @@ index 3950ab4..18e5d80 100644 * pullup (so that all non-inherited RTEs are present) and before * inheritance expansion (so that the info is available for diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c -index e525ba6..6474fbc 100644 +index 8486516..acd6bac 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -38,6 +38,7 @@ @@ -7730,7 +7524,7 @@ index e525ba6..6474fbc 100644 #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/builtins.h" -@@ -3712,6 +3713,10 @@ inline_function(Oid funcid, Oid result_type, List *args, +@@ -3724,6 +3725,10 @@ inline_function(Oid funcid, Oid result_type, List *args, if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) return NULL; @@ -7741,7 +7535,7 @@ index e525ba6..6474fbc 100644 /* * Make a temporary memory context, so that we don't leak all the stuff * that parsing might create. -@@ -4164,7 +4169,8 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) +@@ -4176,7 +4181,8 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) funcform->provolatile == PROVOLATILE_VOLATILE || funcform->prosecdef || !funcform->proretset || @@ -7752,7 +7546,7 @@ index e525ba6..6474fbc 100644 ReleaseSysCache(func_tuple); return NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c -index 6b99a10..4eb9d24 100644 +index 1576613..51e197f 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -25,6 +25,7 @@ @@ -7781,7 +7575,7 @@ index 6b99a10..4eb9d24 100644 rte->modifiedCols = bms_add_member(rte->modifiedCols, diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y -index b793c4d..866e0ba 100644 +index 50ed871..d6b13da 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -183,8 +183,8 @@ static TypeName *TableFuncTypeName(List *columns); @@ -7842,7 +7636,7 @@ index b793c4d..866e0ba 100644 /* ALTER TABLE CLUSTER ON */ | CLUSTER ON name { -@@ -6022,6 +6039,102 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId +@@ -6027,6 +6044,102 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId } ; @@ -7945,7 +7739,7 @@ index b793c4d..866e0ba 100644 /***************************************************************************** * -@@ -10921,6 +11034,7 @@ unreserved_keyword: +@@ -10922,6 +11035,7 @@ unreserved_keyword: | INVOKER | ISOLATION | KEY @@ -7954,7 +7748,7 @@ index b793c4d..866e0ba 100644 | LARGE_P | LAST_P diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c -index 38c7e91..fbc4e9f 100644 +index 1d0fc82..7ad1eeb 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -2302,8 +2302,8 @@ specialAttNum(const char *attname) @@ -7991,7 +7785,7 @@ index 38c7e91..fbc4e9f 100644 } if (attid > rd->rd_att->natts) diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c -index e542dc0..8191f94 100644 +index 7e04a94..da10dc7 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -14,6 +14,7 @@ @@ -8128,7 +7922,7 @@ index e542dc0..8191f94 100644 forboth(lname, names, lvar, vars) { diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c -index 90d5c76..b78f9ba 100644 +index d1e1dac..8e994da 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -53,8 +53,10 @@ @@ -8158,7 +7952,7 @@ index 90d5c76..b78f9ba 100644 Assert(!stmt->ofTypename || !stmt->inhRelations); /* grammar enforces */ -@@ -587,6 +591,9 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, +@@ -599,6 +603,9 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(relation)); @@ -8168,7 +7962,7 @@ index 90d5c76..b78f9ba 100644 tupleDesc = RelationGetDescr(relation); constr = tupleDesc->constr; -@@ -1368,7 +1375,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) +@@ -1380,7 +1387,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) if (constraint->contype == CONSTR_PRIMARY) column->is_not_null = TRUE; } @@ -8177,7 +7971,7 @@ index 90d5c76..b78f9ba 100644 { /* * column will be a system column in the new table, so accept it. -@@ -1945,6 +1952,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) +@@ -1957,6 +1964,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) cxt.inhRelations = NIL; cxt.isalter = true; cxt.hasoids = false; /* need not be right */ @@ -8186,7 +7980,7 @@ index 90d5c76..b78f9ba 100644 cxt.ckconstraints = NIL; cxt.fkconstraints = NIL; diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c -index aa8f2da..604a72c 100644 +index 4666fd6..784723d 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -109,6 +109,7 @@ @@ -8236,18 +8030,17 @@ index aa8f2da..604a72c 100644 /* Reload authentication config files too */ if (!load_hba()) -@@ -2180,6 +2190,10 @@ pmdie(SIGNAL_ARGS) +@@ -2180,6 +2190,9 @@ pmdie(SIGNAL_ARGS) /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); + /* and the security worker too */ + if (SecWorkerPID != 0) + signal_child(SecWorkerPID, SIGTERM); -+ + /* * If we're in recovery, we can't kill the startup process - * right away, because at present doing so does not release -@@ -2241,6 +2255,9 @@ pmdie(SIGNAL_ARGS) +@@ -2242,6 +2255,9 @@ pmdie(SIGNAL_ARGS) /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); @@ -8257,7 +8050,7 @@ index aa8f2da..604a72c 100644 pmState = PM_WAIT_BACKENDS; } -@@ -2276,6 +2293,8 @@ pmdie(SIGNAL_ARGS) +@@ -2277,6 +2293,8 @@ pmdie(SIGNAL_ARGS) signal_child(PgArchPID, SIGQUIT); if (PgStatPID != 0) signal_child(PgStatPID, SIGQUIT); @@ -8266,7 +8059,7 @@ index aa8f2da..604a72c 100644 ExitPostmaster(0); break; } -@@ -2547,6 +2566,16 @@ reaper(SIGNAL_ARGS) +@@ -2549,6 +2567,16 @@ reaper(SIGNAL_ARGS) continue; } @@ -8283,7 +8076,7 @@ index aa8f2da..604a72c 100644 /* * Else do standard backend child cleanup. */ -@@ -2750,6 +2779,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) +@@ -2765,6 +2793,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); } @@ -8302,7 +8095,7 @@ index aa8f2da..604a72c 100644 /* * 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 -@@ -2906,7 +2947,8 @@ PostmasterStateMachine(void) +@@ -2921,7 +2961,8 @@ PostmasterStateMachine(void) WalReceiverPID == 0 && (BgWriterPID == 0 || !FatalError) && WalWriterPID == 0 && @@ -8313,7 +8106,7 @@ index aa8f2da..604a72c 100644 if (FatalError) { diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c -index 68be146..6ba1d2f 100644 +index 06469b3..34f8cbe 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -27,6 +27,7 @@ @@ -8335,7 +8128,7 @@ index 68be146..6ba1d2f 100644 * No rule actions that modify OLD or NEW */ diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c -index c1c5ce9..0f6a446 100644 +index 2bbfc1f..b412ee9 100644 --- a/src/backend/rewrite/rewriteRemove.c +++ b/src/backend/rewrite/rewriteRemove.c @@ -22,6 +22,7 @@ @@ -12986,7 +12779,7 @@ index 0000000..3c4891c +} diff --git a/src/backend/sepgsql/rowlv.c b/src/backend/sepgsql/rowlv.c new file mode 100644 -index 0000000..1078777 +index 0000000..892daf9 --- /dev/null +++ b/src/backend/sepgsql/rowlv.c @@ -0,0 +1,346 @@ @@ -13094,7 +12887,6 @@ index 0000000..1078777 + Form_pg_type typForm; + sepgsql_sid_t tsid; + uint16 tclass; -+ bool result; + + /* object class? */ + switch (RelationGetRelid(rel)) @@ -13145,9 +12937,13 @@ index 0000000..1078777 + case RELKIND_COMPOSITE_TYPE: + tclass = SEPG_CLASS_DB_TUPLE; + break; -+ default: /* index, toast */ ++ case RELKIND_INDEX: ++ case RELKIND_TOASTVALUE: + tclass = SEPG_CLASS_DB_TABLE; + break; ++ default: ++ /* orphan attribute? */ ++ return !sepgsql_get_enforce(); + } + break; + @@ -13172,6 +12968,8 @@ index 0000000..1078777 + switch (get_rel_relkind(typForm->typrelid)) + { + case RELKIND_RELATION: ++ case RELKIND_INDEX: ++ case RELKIND_TOASTVALUE: + tclass = SEPG_CLASS_DB_TABLE; + break; + case RELKIND_SEQUENCE: @@ -13183,9 +12981,9 @@ index 0000000..1078777 + case RELKIND_COMPOSITE_TYPE: + tclass = SEPG_CLASS_DB_TUPLE; + break; -+ default: /* index, toast */ -+ tclass = SEPG_CLASS_DB_TABLE; -+ break; ++ default: ++ /* orphan type? */ ++ return !sepgsql_get_enforce(); + } + } + break; @@ -13199,12 +12997,7 @@ index 0000000..1078777 + tsid.relid = RelationGetRelid(rel); + tsid.secid = HeapTupleGetSecid(tuple); + -+ result = sepgsql_client_perms(tsid, tclass, required, NULL, abort); -+ -+ if (!result) -+ elog(NOTICE, "denied: oid=%u tsid(relid=%u, secid=%u) label=%s tclass=%d required=%u", HeapTupleGetOid(tuple), tsid.relid, tsid.secid, seclabelRawOutput(tsid.relid, tsid.secid), tclass, required); -+ -+ return result; ++ return sepgsql_client_perms(tsid, tclass, required, NULL, abort); +} +#endif + @@ -15225,7 +15018,7 @@ index 0000000..8cd65ae +#endif +} diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c -index ca48cdd..bb0688f 100644 +index 36da56d..6240a62 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) @@ -15246,7 +15039,7 @@ index ca48cdd..bb0688f 100644 /* * dependency on the owner of largeobject diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c -index 575fa86..eb2b5e8 100644 +index 5870081..7ae10b3 100644 --- a/src/backend/tcop/fastpath.c +++ b/src/backend/tcop/fastpath.c @@ -26,6 +26,7 @@ @@ -15257,7 +15050,7 @@ index 575fa86..eb2b5e8 100644 #include "tcop/fastpath.h" #include "tcop/tcopprot.h" #include "utils/acl.h" -@@ -347,6 +348,10 @@ HandleFunctionRequest(StringInfo msgBuf) +@@ -348,6 +349,10 @@ HandleFunctionRequest(StringInfo msgBuf) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fid)); @@ -15266,10 +15059,10 @@ index 575fa86..eb2b5e8 100644 + sepgsql_proc_execute(fid); + /* - * Prepare function call info block and insert arguments. - */ + * Restrict access to pg_get_expr(). This reflects the hack in + * transformFuncCall() in parse_expr.c, see comments there for an diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c -index 8ad4915..54961d8 100644 +index d60dc49..0ef21eb 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -575,7 +575,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) @@ -15282,7 +15075,7 @@ index 8ad4915..54961d8 100644 /* diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c -index 8960246..e8f8ea3 100644 +index ec36644..a25bd00 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -165,6 +165,7 @@ check_xact_readonly(Node *parsetree) @@ -15293,7 +15086,7 @@ index 8960246..e8f8ea3 100644 case T_AlterSeqStmt: case T_AlterTableStmt: case T_RenameStmt: -@@ -696,6 +697,10 @@ standard_ProcessUtility(Node *parsetree, +@@ -697,6 +698,10 @@ standard_ProcessUtility(Node *parsetree, ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); break; @@ -15304,7 +15097,7 @@ index 8960246..e8f8ea3 100644 case T_AlterTableStmt: { List *stmts; -@@ -1760,6 +1765,46 @@ CreateCommandTag(Node *parsetree) +@@ -1761,6 +1766,46 @@ CreateCommandTag(Node *parsetree) } break; @@ -15351,7 +15144,7 @@ index 8960246..e8f8ea3 100644 case T_AlterTableStmt: switch (((AlterTableStmt *) parsetree)->relkind) { -@@ -2352,6 +2397,10 @@ GetCommandLogLevel(Node *parsetree) +@@ -2353,6 +2398,10 @@ GetCommandLogLevel(Node *parsetree) lev = LOGSTMT_DDL; break; @@ -15363,7 +15156,7 @@ index 8960246..e8f8ea3 100644 lev = LOGSTMT_DDL; break; diff --git a/src/backend/tsearch/wparser.c b/src/backend/tsearch/wparser.c -index 0fed35c..ca9e9d1 100644 +index d74256f..f876951 100644 --- a/src/backend/tsearch/wparser.c +++ b/src/backend/tsearch/wparser.c @@ -59,7 +59,7 @@ tt_setup_firstcall(FuncCallContext *funcctx, Oid prsid) @@ -15385,7 +15178,7 @@ index 0fed35c..ca9e9d1 100644 INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "token", diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c -index faad11e..53e6944 100644 +index 79ca6c1..70fc9fb 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -1695,7 +1695,7 @@ aclexplode(PG_FUNCTION_ARGS) @@ -15398,10 +15191,10 @@ index faad11e..53e6944 100644 OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee", diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c -index ed81ced..f3d4b99 100644 +index 8e342a9..2cbd1d9 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c -@@ -4193,7 +4193,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS) +@@ -4200,7 +4200,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS) * build tupdesc for result tuples. This must match this function's * pg_proc entry! */ @@ -15410,7 +15203,7 @@ index ed81ced..f3d4b99 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "abbrev", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "utc_offset", -@@ -4286,7 +4286,7 @@ pg_timezone_names(PG_FUNCTION_ARGS) +@@ -4293,7 +4293,7 @@ pg_timezone_names(PG_FUNCTION_ARGS) * build tupdesc for result tuples. This must match this function's * pg_proc entry! */ @@ -15420,7 +15213,7 @@ index ed81ced..f3d4b99 100644 TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "abbrev", diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c -index a4e0252..36d07b7 100644 +index 8b5def4..9f79a5c 100644 --- a/src/backend/utils/adt/dbsize.c +++ b/src/backend/utils/adt/dbsize.c @@ -21,6 +21,7 @@ @@ -15451,7 +15244,7 @@ index a4e0252..36d07b7 100644 if (tblspcOid == DEFAULTTABLESPACE_OID) snprintf(tblspcPath, MAXPGPATH, "base"); diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c -index e074b79..8bb707f 100644 +index 863727d..d4a5220 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c @@ -25,6 +25,7 @@ @@ -15499,7 +15292,7 @@ index e074b79..8bb707f 100644 if (!fctx->dirdesc) diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c -index 07e6aab..e5c8182 100644 +index c72c4c1..0d23605 100644 --- a/src/backend/utils/adt/lockfuncs.c +++ b/src/backend/utils/adt/lockfuncs.c @@ -85,7 +85,7 @@ pg_lock_status(PG_FUNCTION_ARGS) @@ -15512,7 +15305,7 @@ index 07e6aab..e5c8182 100644 TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database", diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c -index 11342b2..0ddf907 100644 +index 66c8598..c532dea 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -322,7 +322,7 @@ pg_get_keywords(PG_FUNCTION_ARGS) @@ -15525,7 +15318,7 @@ index 11342b2..0ddf907 100644 TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catcode", diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c -index 8379407..d44655b 100644 +index 8b13c8a..09bb38e 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -419,7 +419,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) @@ -15538,7 +15331,7 @@ index 8379407..d44655b 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0); diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c -index 9db070d..6881976 100644 +index 2d0ab44..87eb953 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -30,6 +30,7 @@ @@ -15615,7 +15408,7 @@ index 9db070d..6881976 100644 if (spi_result < 0) elog(ERROR, "SPI_execute_snapshot returned %d", spi_result); diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c -index c837e67..1bf4eb8 100644 +index 8c18a26..843d702 100644 --- a/src/backend/utils/adt/tid.c +++ b/src/backend/utils/adt/tid.c @@ -27,6 +27,7 @@ @@ -15645,7 +15438,7 @@ index c837e67..1bf4eb8 100644 if (rel->rd_rel->relkind == RELKIND_VIEW) return currtid_for_view(rel, tid); diff --git a/src/backend/utils/adt/trigfuncs.c b/src/backend/utils/adt/trigfuncs.c -index 70246fb..b996537 100644 +index d831b60..badc1f5 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) @@ -15660,7 +15453,7 @@ index 70246fb..b996537 100644 if (newtuple->t_len == oldtuple->t_len && newheader->t_hoff == oldheader->t_hoff && diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c -index 78f08f4..4a44810 100644 +index 94c242d..b44cb63 100644 --- a/src/backend/utils/adt/tsvector_op.c +++ b/src/backend/utils/adt/tsvector_op.c @@ -975,7 +975,7 @@ ts_setup_firstcall(FunctionCallInfo fcinfo, FuncCallContext *funcctx, @@ -15673,10 +15466,10 @@ index 78f08f4..4a44810 100644 TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "ndoc", diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c -index 88d8d8b..281acb0 100644 +index 06e2549..56c5df9 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c -@@ -924,12 +924,12 @@ PlanCacheComputeResultDesc(List *stmt_list) +@@ -926,12 +926,12 @@ PlanCacheComputeResultDesc(List *stmt_list) if (IsA(node, Query)) { query = (Query *) node; @@ -15691,7 +15484,7 @@ index 88d8d8b..281acb0 100644 } /* other cases shouldn't happen, but return NULL */ break; -@@ -940,13 +940,13 @@ PlanCacheComputeResultDesc(List *stmt_list) +@@ -942,13 +942,13 @@ PlanCacheComputeResultDesc(List *stmt_list) { query = (Query *) node; Assert(query->returningList); @@ -15708,10 +15501,10 @@ index 88d8d8b..281acb0 100644 /* 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 d462510..c704bc9 100644 +index 510f077..430d89e 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c -@@ -216,7 +216,7 @@ static void write_relcache_init_file(bool shared); +@@ -215,7 +215,7 @@ static void write_relcache_init_file(bool shared); static void write_item(const void *data, Size len, FILE *fp); static void formrdesc(const char *relationName, Oid relationReltype, @@ -15720,7 +15513,7 @@ index d462510..c704bc9 100644 int natts, const FormData_pg_attribute *attrs); static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK); -@@ -355,7 +355,8 @@ AllocateRelationDesc(Form_pg_class relp) +@@ -354,7 +354,8 @@ AllocateRelationDesc(Form_pg_class relp) /* and allocate attribute tuple form storage */ relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, @@ -15730,7 +15523,7 @@ index d462510..c704bc9 100644 /* which we mark as a reference-counted tupdesc */ relation->rd_att->tdrefcount = 1; -@@ -437,6 +438,7 @@ RelationBuildTupleDesc(Relation relation) +@@ -436,6 +437,7 @@ RelationBuildTupleDesc(Relation relation) relation->rd_att->tdtypeid = relation->rd_rel->reltype; relation->rd_att->tdtypmod = -1; /* unnecessary, but... */ relation->rd_att->tdhasoid = relation->rd_rel->relhasoids; @@ -15738,7 +15531,7 @@ index d462510..c704bc9 100644 constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext, sizeof(TupleConstr)); -@@ -1396,7 +1398,7 @@ LookupOpclassInfo(Oid operatorClassOid, +@@ -1395,7 +1397,7 @@ LookupOpclassInfo(Oid operatorClassOid, */ static void formrdesc(const char *relationName, Oid relationReltype, @@ -15747,7 +15540,7 @@ index d462510..c704bc9 100644 int natts, const FormData_pg_attribute *attrs) { Relation relation; -@@ -1459,6 +1461,7 @@ formrdesc(const char *relationName, Oid relationReltype, +@@ -1458,6 +1460,7 @@ formrdesc(const char *relationName, Oid relationReltype, relation->rd_rel->reltuples = 1; relation->rd_rel->relkind = RELKIND_RELATION; relation->rd_rel->relhasoids = hasoids; @@ -15755,7 +15548,7 @@ index d462510..c704bc9 100644 relation->rd_rel->relnatts = (int16) natts; /* -@@ -1468,7 +1471,7 @@ formrdesc(const char *relationName, Oid relationReltype, +@@ -1467,7 +1470,7 @@ formrdesc(const char *relationName, Oid relationReltype, * because it will never be replaced. The data comes from * src/include/catalog/ headers via genbki.pl. */ @@ -15764,7 +15557,7 @@ index d462510..c704bc9 100644 relation->rd_att->tdrefcount = 1; /* mark as refcounted */ relation->rd_att->tdtypeid = relationReltype; -@@ -2551,6 +2554,7 @@ RelationBuildLocalRelation(const char *relname, +@@ -2542,6 +2545,7 @@ RelationBuildLocalRelation(const char *relname, rel->rd_rel->relkind = RELKIND_UNCATALOGED; rel->rd_rel->relhasoids = rel->rd_att->tdhasoid; @@ -15772,7 +15565,7 @@ index d462510..c704bc9 100644 rel->rd_rel->relnatts = natts; rel->rd_rel->reltype = InvalidOid; /* needed when bootstrapping: */ -@@ -2793,11 +2797,11 @@ RelationCacheInitializePhase2(void) +@@ -2784,11 +2788,11 @@ RelationCacheInitializePhase2(void) if (!load_relcache_init_file(true)) { formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true, @@ -15787,7 +15580,7 @@ index d462510..c704bc9 100644 #define NUM_CRITICAL_SHARED_RELS 3 /* fix if you change list above */ } -@@ -2848,13 +2852,13 @@ RelationCacheInitializePhase3(void) +@@ -2839,13 +2843,13 @@ RelationCacheInitializePhase3(void) needNewCacheFile = true; formrdesc("pg_class", RelationRelation_Rowtype_Id, false, @@ -15805,7 +15598,7 @@ index d462510..c704bc9 100644 #define NUM_CRITICAL_LOCAL_RELS 4 /* fix if you change list above */ } -@@ -3007,6 +3011,7 @@ RelationCacheInitializePhase3(void) +@@ -2998,6 +3002,7 @@ RelationCacheInitializePhase3(void) Assert(relation->rd_att->tdtypeid == relp->reltype); Assert(relation->rd_att->tdtypmod == -1); Assert(relation->rd_att->tdhasoid == relp->relhasoids); @@ -15813,7 +15606,7 @@ index d462510..c704bc9 100644 ReleaseSysCache(htup); -@@ -3117,7 +3122,7 @@ load_critical_index(Oid indexoid, Oid heapoid) +@@ -3108,7 +3113,7 @@ load_critical_index(Oid indexoid, Oid heapoid) */ static TupleDesc BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, @@ -15822,7 +15615,7 @@ index d462510..c704bc9 100644 { TupleDesc result; MemoryContext oldcxt; -@@ -3125,7 +3130,7 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, +@@ -3116,7 +3121,7 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, oldcxt = MemoryContextSwitchTo(CacheMemoryContext); @@ -15831,7 +15624,7 @@ index d462510..c704bc9 100644 result->tdtypeid = RECORDOID; /* not right, but we don't care */ result->tdtypmod = -1; -@@ -3155,7 +3160,7 @@ GetPgClassDescriptor(void) +@@ -3146,7 +3151,7 @@ GetPgClassDescriptor(void) if (pgclassdesc == NULL) pgclassdesc = BuildHardcodedDescriptor(Natts_pg_class, Desc_pg_class, @@ -15840,7 +15633,7 @@ index d462510..c704bc9 100644 return pgclassdesc; } -@@ -3169,7 +3174,7 @@ GetPgIndexDescriptor(void) +@@ -3160,7 +3165,7 @@ GetPgIndexDescriptor(void) if (pgindexdesc == NULL) pgindexdesc = BuildHardcodedDescriptor(Natts_pg_index, Desc_pg_index, @@ -15849,7 +15642,7 @@ index d462510..c704bc9 100644 return pgindexdesc; } -@@ -3985,7 +3990,8 @@ load_relcache_init_file(bool shared) +@@ -3976,7 +3981,8 @@ load_relcache_init_file(bool shared) /* initialize attribute tuple forms */ rel->rd_att = CreateTemplateTupleDesc(relform->relnatts, @@ -15860,7 +15653,7 @@ index d462510..c704bc9 100644 rel->rd_att->tdtypeid = relform->reltype; diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c -index 61b06ac..b08654b 100644 +index 0224cfb..3137199 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -895,6 +895,30 @@ GetSysCacheOid(int cacheId, @@ -15895,7 +15688,7 @@ index 61b06ac..b08654b 100644 /* * SearchSysCacheAttName diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c -index c3c0440..fb5b762 100644 +index 04f91f1..6f82199 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -24,6 +24,7 @@ @@ -15974,7 +15767,7 @@ index c3c0440..fb5b762 100644 return result; } diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c -index 382008c..e6eba47 100644 +index d946aab..f7581a7 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -1105,7 +1105,7 @@ build_function_result_tupdesc_d(Datum proallargtypes, @@ -15996,7 +15789,7 @@ index 382008c..e6eba47 100644 (AttrNumber) 1, attname, diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c -index 179d009..4bd7314 100644 +index fc97683..1fcb33f 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -37,6 +37,7 @@ @@ -16007,7 +15800,7 @@ index 179d009..4bd7314 100644 #include "storage/bufmgr.h" #include "storage/fd.h" #include "storage/ipc.h" -@@ -304,6 +305,9 @@ CheckMyDatabase(const char *name, bool am_superuser) +@@ -306,6 +307,9 @@ CheckMyDatabase(const char *name, bool am_superuser) errmsg("permission denied for database \"%s\"", name), errdetail("User does not have CONNECT privilege."))); @@ -16017,7 +15810,7 @@ index 179d009..4bd7314 100644 /* * Check connection limit for this database. * -@@ -800,6 +804,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, +@@ -817,6 +821,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, /* set up ACL framework (so CheckMyDatabase can check permissions) */ initialize_acl(); @@ -16028,7 +15821,7 @@ index 179d009..4bd7314 100644 * Re-read the 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 920daeb..44fd3a9 100644 +index 4e55b16..a9f9f7e 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -31,6 +31,7 @@ @@ -16045,9 +15838,9 @@ index 920daeb..44fd3a9 100644 #include "replication/walsender.h" +#include "sepgsql/sepgsql.h" #include "storage/bufmgr.h" + #include "storage/standby.h" #include "storage/fd.h" - #include "tcop/tcopprot.h" -@@ -337,6 +339,18 @@ static const struct config_enum_entry constraint_exclusion_options[] = { +@@ -335,6 +337,18 @@ static const struct config_enum_entry constraint_exclusion_options[] = { {NULL, 0, false} }; @@ -16066,7 +15859,7 @@ index 920daeb..44fd3a9 100644 /* * Options for enum values stored in other modules */ -@@ -366,6 +380,7 @@ bool log_btree_build_stats = false; +@@ -364,6 +378,7 @@ bool log_btree_build_stats = false; bool check_function_bodies = true; bool default_with_oids = false; @@ -16074,7 +15867,7 @@ index 920daeb..44fd3a9 100644 bool SQL_inheritance = true; bool Password_encryption = true; -@@ -1094,6 +1109,14 @@ static struct config_bool ConfigureNamesBool[] = +@@ -1103,6 +1118,14 @@ static struct config_bool ConfigureNamesBool[] = false, NULL, NULL }, { @@ -16089,7 +15882,7 @@ index 920daeb..44fd3a9 100644 {"logging_collector", PGC_POSTMASTER, LOGGING_WHERE, gettext_noop("Start a subprocess to capture stderr output and/or csvlogs into log files."), NULL -@@ -1261,6 +1284,36 @@ static struct config_bool ConfigureNamesBool[] = +@@ -1270,6 +1293,36 @@ static struct config_bool ConfigureNamesBool[] = false, NULL, NULL }, @@ -16126,7 +15919,7 @@ index 920daeb..44fd3a9 100644 /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL -@@ -2820,7 +2873,16 @@ static struct config_enum ConfigureNamesEnum[] = +@@ -2844,7 +2897,16 @@ static struct config_enum ConfigureNamesEnum[] = &xmloption, XMLOPTION_CONTENT, xmloption_options, NULL, NULL }, @@ -16144,7 +15937,7 @@ index 920daeb..44fd3a9 100644 /* End-of-list marker */ { -@@ -6113,7 +6175,7 @@ GetPGVariableResultDesc(const char *name) +@@ -6139,7 +6201,7 @@ GetPGVariableResultDesc(const char *name) if (guc_name_compare(name, "all") == 0) { /* need a tuple descriptor representing three TEXT columns */ @@ -16153,7 +15946,7 @@ index 920daeb..44fd3a9 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", -@@ -6129,7 +6191,7 @@ GetPGVariableResultDesc(const char *name) +@@ -6155,7 +6217,7 @@ GetPGVariableResultDesc(const char *name) (void) GetConfigOptionByName(name, &varname); /* need a tuple descriptor representing a single TEXT column */ @@ -16162,7 +15955,7 @@ index 920daeb..44fd3a9 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, TEXTOID, -1, 0); } -@@ -6152,7 +6214,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest) +@@ -6178,7 +6240,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest) value = GetConfigOptionByName(name, &varname); /* need a tuple descriptor representing a single TEXT column */ @@ -16171,7 +15964,7 @@ index 920daeb..44fd3a9 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, TEXTOID, -1, 0); -@@ -6179,7 +6241,7 @@ ShowAllGUCConfig(DestReceiver *dest) +@@ -6205,7 +6267,7 @@ ShowAllGUCConfig(DestReceiver *dest) bool isnull[3] = {false, false, false}; /* need a tuple descriptor representing three TEXT columns */ @@ -16180,7 +15973,7 @@ index 920daeb..44fd3a9 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", -@@ -6574,7 +6636,7 @@ show_all_settings(PG_FUNCTION_ARGS) +@@ -6600,7 +6662,7 @@ show_all_settings(PG_FUNCTION_ARGS) * need a tuple descriptor representing NUM_PG_SETTINGS_ATTS columns * of the appropriate types */ @@ -16190,7 +15983,7 @@ index 920daeb..44fd3a9 100644 TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample -index 2ed8412..10f8947 100644 +index a3b1457..b78c6d9 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -76,6 +76,8 @@ @@ -16203,10 +15996,10 @@ index 2ed8412..10f8947 100644 #ssl = off # (change requires restart) #ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH' # allowed SSL ciphers diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c -index ac62d45..f05a094 100644 +index 19c594d..b80b6ac 100644 --- a/src/backend/utils/mmgr/portalmem.c +++ b/src/backend/utils/mmgr/portalmem.c -@@ -894,7 +894,7 @@ pg_cursor(PG_FUNCTION_ARGS) +@@ -949,7 +949,7 @@ pg_cursor(PG_FUNCTION_ARGS) * build tupdesc for result tuples. This must match the definition of the * pg_cursors view in system_views.sql */ @@ -16216,7 +16009,7 @@ index ac62d45..f05a094 100644 TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c -index f40ad87..497bdf0 100644 +index 0aee70d..ceb9a87 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -87,6 +87,7 @@ static bool debug = false; @@ -16268,7 +16061,7 @@ index f40ad87..497bdf0 100644 show_setting = true; break; diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h -index 14fe652..40b7b40 100644 +index 5a73779..67fce14 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -103,6 +103,7 @@ typedef struct _restoreOptions @@ -16289,7 +16082,7 @@ index 14fe652..40b7b40 100644 const char *defn, const char *dropStmt, const char *copyStmt, diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c -index 6bbe2de..9da50b0 100644 +index be5339e..8352c93 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -540,6 +540,8 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, @@ -16345,7 +16138,7 @@ index 6bbe2de..9da50b0 100644 newToc->desc = strdup(desc); newToc->defn = strdup(defn); newToc->dropStmt = strdup(dropStmt); -@@ -2027,6 +2043,7 @@ WriteToc(ArchiveHandle *AH) +@@ -2046,6 +2062,7 @@ WriteToc(ArchiveHandle *AH) WriteStr(AH, te->tablespace); WriteStr(AH, te->owner); WriteStr(AH, te->withOids ? "true" : "false"); @@ -16353,7 +16146,7 @@ index 6bbe2de..9da50b0 100644 /* Dump list of dependencies */ for (i = 0; i < te->nDeps; i++) -@@ -2138,6 +2155,16 @@ ReadToc(ArchiveHandle *AH) +@@ -2157,6 +2174,16 @@ ReadToc(ArchiveHandle *AH) else te->withOids = true; @@ -16370,7 +16163,7 @@ index 6bbe2de..9da50b0 100644 /* Read TOC entry dependencies */ if (AH->version >= K_VERS_1_5) { -@@ -2256,6 +2283,9 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) +@@ -2275,6 +2302,9 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te)) return 0; @@ -16378,9 +16171,9 @@ index 6bbe2de..9da50b0 100644 + return 0; + /* Ignore DATABASE entry unless we should create it */ - if (!ropt->create && strcmp(te->desc, "DATABASE") == 0) + if (!ropt->createDB && strcmp(te->desc, "DATABASE") == 0) return 0; -@@ -2322,6 +2352,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) +@@ -2341,6 +2371,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) (strcmp(te->desc, "ACL") == 0 && strncmp(te->tag, "LARGE OBJECT ", 13) == 0) || (strcmp(te->desc, "COMMENT") == 0 && @@ -16389,7 +16182,7 @@ index 6bbe2de..9da50b0 100644 strncmp(te->tag, "LARGE OBJECT ", 13) == 0)) res = res & REQ_DATA; else -@@ -2473,6 +2505,36 @@ _doSetWithOids(ArchiveHandle *AH, const bool withOids) +@@ -2492,6 +2524,36 @@ _doSetWithOids(ArchiveHandle *AH, const bool withOids) destroyPQExpBuffer(cmd); } @@ -16426,7 +16219,7 @@ index 6bbe2de..9da50b0 100644 /* * Issue the commands to connect to the specified database. -@@ -2571,6 +2633,18 @@ _setWithOids(ArchiveHandle *AH, TocEntry *te) +@@ -2590,6 +2652,18 @@ _setWithOids(ArchiveHandle *AH, TocEntry *te) } } @@ -16445,7 +16238,7 @@ index 6bbe2de..9da50b0 100644 /* * Issue the commands to select the specified schema as the current schema -@@ -2808,9 +2882,12 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat +@@ -2827,9 +2901,12 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat _selectOutputSchema(AH, te->namespace); _selectTablespace(AH, te->tablespace); @@ -16460,7 +16253,7 @@ index 6bbe2de..9da50b0 100644 /* Emit header comment for item */ if (!AH->noTocComments) diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h -index 2e944c1..67f62ac 100644 +index 0a135ee..27b2ef5 100644 --- a/src/bin/pg_dump/pg_backup_archiver.h +++ b/src/bin/pg_dump/pg_backup_archiver.h @@ -63,7 +63,7 @@ typedef z_stream *z_streamp; @@ -16502,7 +16295,7 @@ index 2e944c1..67f62ac 100644 char *defn; char *dropStmt; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c -index 2431d71..e7ef296 100644 +index 7a06f9b..f7fa80a 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -125,7 +125,7 @@ static int binary_upgrade = 0; @@ -16552,7 +16345,7 @@ index 2431d71..e7ef296 100644 else { fprintf(stderr, -@@ -515,6 +523,10 @@ main(int argc, char **argv) +@@ -516,6 +524,10 @@ main(int argc, char **argv) exit(1); } @@ -16563,7 +16356,7 @@ index 2431d71..e7ef296 100644 /* open the output file */ if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0) { -@@ -833,6 +845,7 @@ help(const char *progname) +@@ -834,6 +846,7 @@ help(const char *progname) printf(_(" --use-set-session-authorization\n" " use SET SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n")); @@ -16571,7 +16364,7 @@ index 2431d71..e7ef296 100644 printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); -@@ -1127,7 +1140,7 @@ dumpTableData_copy(Archive *fout, void *dcontext) +@@ -1128,7 +1141,7 @@ dumpTableData_copy(Archive *fout, void *dcontext) * cases involving ADD COLUMN and inheritance.) */ if (g_fout->remoteVersion >= 70300) @@ -16580,7 +16373,7 @@ index 2431d71..e7ef296 100644 else column_list = ""; /* can't select columns in COPY */ -@@ -1251,14 +1264,16 @@ dumpTableData_insert(Archive *fout, void *dcontext) +@@ -1252,14 +1265,16 @@ dumpTableData_insert(Archive *fout, void *dcontext) if (fout->remoteVersion >= 70100) { appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " @@ -16599,7 +16392,7 @@ index 2431d71..e7ef296 100644 fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, classname)); } -@@ -1398,7 +1413,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) +@@ -1399,7 +1414,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) appendPQExpBuffer(copyBuf, "COPY %s ", fmtId(tbinfo->dobj.name)); appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n", @@ -16608,7 +16401,7 @@ index 2431d71..e7ef296 100644 (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : ""); copyStmt = copyBuf->data; } -@@ -1412,7 +1427,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) +@@ -1413,7 +1428,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId, tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -16617,7 +16410,7 @@ index 2431d71..e7ef296 100644 "", "", copyStmt, tdinfo->dobj.dependencies, tdinfo->dobj.nDeps, dumpFn, tdinfo); -@@ -1457,6 +1472,7 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids) +@@ -1458,6 +1473,7 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids) tdinfo->dobj.namespace = tblinfo[i].dobj.namespace; tdinfo->tdtable = &(tblinfo[i]); tdinfo->oids = oids; @@ -16625,7 +16418,7 @@ index 2431d71..e7ef296 100644 addObjectDependency(&tdinfo->dobj, tblinfo[i].dobj.dumpId); tblinfo[i].dataObj = tdinfo; -@@ -1780,6 +1796,7 @@ dumpDatabase(Archive *AH) +@@ -1781,6 +1797,7 @@ dumpDatabase(Archive *AH) NULL, /* Tablespace */ dba, /* Owner */ false, /* with oids */ @@ -16633,7 +16426,7 @@ index 2431d71..e7ef296 100644 "DATABASE", /* Desc */ SECTION_PRE_DATA, /* Section */ creaQry->data, /* Create */ -@@ -1825,7 +1842,7 @@ dumpDatabase(Archive *AH) +@@ -1826,7 +1843,7 @@ dumpDatabase(Archive *AH) LargeObjectRelationId); ArchiveEntry(AH, nilCatalogId, createDumpId(), "pg_largeobject", NULL, NULL, "", @@ -16642,7 +16435,7 @@ index 2431d71..e7ef296 100644 loOutQry->data, "", NULL, NULL, 0, NULL, NULL); -@@ -1857,7 +1874,7 @@ dumpDatabase(Archive *AH) +@@ -1858,7 +1875,7 @@ dumpDatabase(Archive *AH) appendPQExpBuffer(dbQry, ";\n"); ArchiveEntry(AH, dbCatId, createDumpId(), datname, NULL, NULL, @@ -16651,7 +16444,7 @@ index 2431d71..e7ef296 100644 dbQry->data, "", NULL, &dbDumpId, 1, NULL, NULL); } -@@ -1896,7 +1913,7 @@ dumpEncoding(Archive *AH) +@@ -1897,7 +1914,7 @@ dumpEncoding(Archive *AH) ArchiveEntry(AH, nilCatalogId, createDumpId(), "ENCODING", NULL, NULL, "", @@ -16660,7 +16453,7 @@ index 2431d71..e7ef296 100644 qry->data, "", NULL, NULL, 0, NULL, NULL); -@@ -1923,7 +1940,7 @@ dumpStdStrings(Archive *AH) +@@ -1924,7 +1941,7 @@ dumpStdStrings(Archive *AH) ArchiveEntry(AH, nilCatalogId, createDumpId(), "STDSTRINGS", NULL, NULL, "", @@ -16669,7 +16462,7 @@ index 2431d71..e7ef296 100644 qry->data, "", NULL, NULL, 0, NULL, NULL); -@@ -1956,16 +1973,17 @@ getBlobs(Archive *AH) +@@ -1957,16 +1974,17 @@ getBlobs(Archive *AH) /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (AH->remoteVersion >= 90000) appendPQExpBuffer(blobQry, @@ -16691,7 +16484,7 @@ index 2431d71..e7ef296 100644 " FROM pg_class WHERE relkind = 'l'"); res = PQexec(g_conn, blobQry->data); -@@ -1995,6 +2013,11 @@ getBlobs(Archive *AH) +@@ -1996,6 +2014,11 @@ getBlobs(Archive *AH) binfo[i].blobacl = strdup(PQgetvalue(res, i, 2)); else binfo[i].blobacl = NULL; @@ -16703,7 +16496,7 @@ index 2431d71..e7ef296 100644 } /* -@@ -2034,7 +2057,7 @@ dumpBlob(Archive *AH, BlobInfo *binfo) +@@ -2035,7 +2058,7 @@ dumpBlob(Archive *AH, BlobInfo *binfo) ArchiveEntry(AH, binfo->dobj.catId, binfo->dobj.dumpId, binfo->dobj.name, NULL, NULL, @@ -16712,7 +16505,7 @@ index 2431d71..e7ef296 100644 "BLOB", SECTION_PRE_DATA, cquery->data, dquery->data, NULL, binfo->dobj.dependencies, binfo->dobj.nDeps, -@@ -2049,6 +2072,10 @@ dumpBlob(Archive *AH, BlobInfo *binfo) +@@ -2050,6 +2073,10 @@ dumpBlob(Archive *AH, BlobInfo *binfo) NULL, binfo->rolname, binfo->dobj.catId, 0, binfo->dobj.dumpId); @@ -16723,7 +16516,7 @@ index 2431d71..e7ef296 100644 /* Dump ACL if any */ if (binfo->blobacl) dumpACL(AH, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT", -@@ -2356,6 +2383,7 @@ getNamespaces(int *numNamespaces) +@@ -2357,6 +2384,7 @@ getNamespaces(int *numNamespaces) int i_nspname; int i_rolname; int i_nspacl; @@ -16731,7 +16524,7 @@ index 2431d71..e7ef296 100644 /* * Before 7.3, there are no real namespaces; create two dummy entries, one -@@ -2372,6 +2400,7 @@ getNamespaces(int *numNamespaces) +@@ -2373,6 +2401,7 @@ getNamespaces(int *numNamespaces) nsinfo[0].dobj.name = strdup("public"); nsinfo[0].rolname = strdup(""); nsinfo[0].nspacl = strdup(""); @@ -16739,7 +16532,7 @@ index 2431d71..e7ef296 100644 selectDumpableNamespace(&nsinfo[0]); -@@ -2382,6 +2411,7 @@ getNamespaces(int *numNamespaces) +@@ -2383,6 +2412,7 @@ getNamespaces(int *numNamespaces) nsinfo[1].dobj.name = strdup("pg_catalog"); nsinfo[1].rolname = strdup(""); nsinfo[1].nspacl = strdup(""); @@ -16747,7 +16540,7 @@ index 2431d71..e7ef296 100644 selectDumpableNamespace(&nsinfo[1]); -@@ -2402,8 +2432,8 @@ getNamespaces(int *numNamespaces) +@@ -2403,8 +2433,8 @@ getNamespaces(int *numNamespaces) */ appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, " "(%s nspowner) AS rolname, " @@ -16758,7 +16551,7 @@ index 2431d71..e7ef296 100644 res = PQexec(g_conn, query->data); check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); -@@ -2417,6 +2447,7 @@ getNamespaces(int *numNamespaces) +@@ -2418,6 +2448,7 @@ getNamespaces(int *numNamespaces) i_nspname = PQfnumber(res, "nspname"); i_rolname = PQfnumber(res, "rolname"); i_nspacl = PQfnumber(res, "nspacl"); @@ -16766,7 +16559,7 @@ index 2431d71..e7ef296 100644 for (i = 0; i < ntups; i++) { -@@ -2427,6 +2458,7 @@ getNamespaces(int *numNamespaces) +@@ -2428,6 +2459,7 @@ getNamespaces(int *numNamespaces) nsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_nspname)); nsinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname)); nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl)); @@ -16774,7 +16567,7 @@ index 2431d71..e7ef296 100644 /* Decide whether to dump this namespace */ selectDumpableNamespace(&nsinfo[i]); -@@ -2515,6 +2547,7 @@ getTypes(int *numTypes) +@@ -2516,6 +2548,7 @@ getTypes(int *numTypes) int i_typtype; int i_typisdefined; int i_isarray; @@ -16782,7 +16575,7 @@ index 2431d71..e7ef296 100644 /* * we include even the built-in types because those may be used as array -@@ -2547,8 +2580,10 @@ getTypes(int *numTypes) +@@ -2548,8 +2581,10 @@ getTypes(int *numTypes) "typtype, typisdefined, " "typname[0] = '_' AND typelem != 0 AND " "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray " @@ -16794,7 +16587,7 @@ index 2431d71..e7ef296 100644 } else if (g_fout->remoteVersion >= 70300) { -@@ -2561,6 +2596,7 @@ getTypes(int *numTypes) +@@ -2562,6 +2597,7 @@ getTypes(int *numTypes) "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " "typtype, typisdefined, " "typname[0] = '_' AND typelem != 0 AS isarray " @@ -16802,7 +16595,7 @@ index 2431d71..e7ef296 100644 "FROM pg_type", username_subquery); } -@@ -2575,6 +2611,7 @@ getTypes(int *numTypes) +@@ -2576,6 +2612,7 @@ getTypes(int *numTypes) "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " "typtype, typisdefined, " "typname[0] = '_' AND typelem != 0 AS isarray " @@ -16810,7 +16603,7 @@ index 2431d71..e7ef296 100644 "FROM pg_type", username_subquery); } -@@ -2591,6 +2628,7 @@ getTypes(int *numTypes) +@@ -2592,6 +2629,7 @@ getTypes(int *numTypes) "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " "typtype, typisdefined, " "typname[0] = '_' AND typelem != 0 AS isarray " @@ -16818,7 +16611,7 @@ index 2431d71..e7ef296 100644 "FROM pg_type", username_subquery); } -@@ -2615,6 +2653,7 @@ getTypes(int *numTypes) +@@ -2616,6 +2654,7 @@ getTypes(int *numTypes) i_typtype = PQfnumber(res, "typtype"); i_typisdefined = PQfnumber(res, "typisdefined"); i_isarray = PQfnumber(res, "isarray"); @@ -16826,7 +16619,7 @@ index 2431d71..e7ef296 100644 for (i = 0; i < ntups; i++) { -@@ -2642,6 +2681,8 @@ getTypes(int *numTypes) +@@ -2643,6 +2682,8 @@ getTypes(int *numTypes) else tyinfo[i].isArray = false; @@ -16835,7 +16628,7 @@ index 2431d71..e7ef296 100644 /* Decide whether we want to dump it */ selectDumpableType(&tyinfo[i]); -@@ -3407,6 +3448,7 @@ getTables(int *numTables) +@@ -3408,6 +3449,7 @@ getTables(int *numTables) int i_relhasindex; int i_relhasrules; int i_relhasoids; @@ -16843,7 +16636,7 @@ index 2431d71..e7ef296 100644 int i_relfrozenxid; int i_owning_tab; int i_owning_col; -@@ -3414,6 +3456,7 @@ getTables(int *numTables) +@@ -3415,6 +3457,7 @@ getTables(int *numTables) int i_reloptions; int i_toastreloptions; int i_reloftype; @@ -16851,7 +16644,7 @@ index 2431d71..e7ef296 100644 /* Make sure we are in proper schema */ selectSourceSchema("pg_catalog"); -@@ -3450,6 +3493,7 @@ getTables(int *numTables) +@@ -3451,6 +3494,7 @@ getTables(int *numTables) "(%s c.relowner) AS rolname, " "c.relchecks, c.relhastriggers, " "c.relhasindex, c.relhasrules, c.relhasoids, " @@ -16859,7 +16652,7 @@ index 2431d71..e7ef296 100644 "c.relfrozenxid, " "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " "d.refobjid AS owning_tab, " -@@ -3457,6 +3501,7 @@ getTables(int *numTables) +@@ -3458,6 +3502,7 @@ getTables(int *numTables) "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "array_to_string(c.reloptions, ', ') AS reloptions, " "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " @@ -16867,7 +16660,7 @@ index 2431d71..e7ef296 100644 "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " -@@ -3467,6 +3512,7 @@ getTables(int *numTables) +@@ -3468,6 +3513,7 @@ getTables(int *numTables) "WHERE c.relkind in ('%c', '%c', '%c', '%c') " "ORDER BY c.oid", username_subquery, @@ -16875,7 +16668,7 @@ index 2431d71..e7ef296 100644 RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); -@@ -3483,6 +3529,7 @@ getTables(int *numTables) +@@ -3484,6 +3530,7 @@ getTables(int *numTables) "(%s c.relowner) AS rolname, " "c.relchecks, c.relhastriggers, " "c.relhasindex, c.relhasrules, c.relhasoids, " @@ -16883,7 +16676,7 @@ index 2431d71..e7ef296 100644 "c.relfrozenxid, " "NULL AS reloftype, " "d.refobjid AS owning_tab, " -@@ -3490,6 +3537,7 @@ getTables(int *numTables) +@@ -3491,6 +3538,7 @@ getTables(int *numTables) "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "array_to_string(c.reloptions, ', ') AS reloptions, " "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " @@ -16891,7 +16684,7 @@ index 2431d71..e7ef296 100644 "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " -@@ -3500,6 +3548,7 @@ getTables(int *numTables) +@@ -3501,6 +3549,7 @@ getTables(int *numTables) "WHERE c.relkind in ('%c', '%c', '%c', '%c') " "ORDER BY c.oid", username_subquery, @@ -16899,7 +16692,7 @@ index 2431d71..e7ef296 100644 RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); -@@ -3516,6 +3565,7 @@ getTables(int *numTables) +@@ -3517,6 +3566,7 @@ getTables(int *numTables) "(%s relowner) AS rolname, " "relchecks, (reltriggers <> 0) AS relhastriggers, " "relhasindex, relhasrules, relhasoids, " @@ -16907,7 +16700,7 @@ index 2431d71..e7ef296 100644 "relfrozenxid, " "NULL AS reloftype, " "d.refobjid AS owning_tab, " -@@ -3523,6 +3573,7 @@ getTables(int *numTables) +@@ -3524,6 +3574,7 @@ getTables(int *numTables) "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "array_to_string(c.reloptions, ', ') AS reloptions, " "NULL AS toast_reloptions " @@ -16915,7 +16708,7 @@ index 2431d71..e7ef296 100644 "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " -@@ -3548,6 +3599,7 @@ getTables(int *numTables) +@@ -3549,6 +3600,7 @@ getTables(int *numTables) "(%s relowner) AS rolname, " "relchecks, (reltriggers <> 0) AS relhastriggers, " "relhasindex, relhasrules, relhasoids, " @@ -16923,7 +16716,7 @@ index 2431d71..e7ef296 100644 "0 AS relfrozenxid, " "NULL AS reloftype, " "d.refobjid AS owning_tab, " -@@ -3555,6 +3607,7 @@ getTables(int *numTables) +@@ -3556,6 +3608,7 @@ getTables(int *numTables) "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "NULL AS reloptions, " "NULL AS toast_reloptions " @@ -16931,7 +16724,7 @@ index 2431d71..e7ef296 100644 "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " -@@ -3580,6 +3633,7 @@ getTables(int *numTables) +@@ -3581,6 +3634,7 @@ getTables(int *numTables) "(%s relowner) AS rolname, " "relchecks, (reltriggers <> 0) AS relhastriggers, " "relhasindex, relhasrules, relhasoids, " @@ -16939,7 +16732,7 @@ index 2431d71..e7ef296 100644 "0 AS relfrozenxid, " "NULL AS reloftype, " "d.refobjid AS owning_tab, " -@@ -3587,6 +3641,7 @@ getTables(int *numTables) +@@ -3588,6 +3642,7 @@ getTables(int *numTables) "NULL AS reltablespace, " "NULL AS reloptions, " "NULL AS toast_reloptions " @@ -16947,7 +16740,7 @@ index 2431d71..e7ef296 100644 "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " -@@ -3608,6 +3663,7 @@ getTables(int *numTables) +@@ -3609,6 +3664,7 @@ getTables(int *numTables) "(%s relowner) AS rolname, " "relchecks, (reltriggers <> 0) AS relhastriggers, " "relhasindex, relhasrules, relhasoids, " @@ -16955,7 +16748,7 @@ index 2431d71..e7ef296 100644 "0 AS relfrozenxid, " "NULL AS reloftype, " "NULL::oid AS owning_tab, " -@@ -3615,6 +3671,7 @@ getTables(int *numTables) +@@ -3616,6 +3672,7 @@ getTables(int *numTables) "NULL AS reltablespace, " "NULL AS reloptions, " "NULL AS toast_reloptions " @@ -16963,7 +16756,7 @@ index 2431d71..e7ef296 100644 "FROM pg_class " "WHERE relkind IN ('%c', '%c', '%c') " "ORDER BY oid", -@@ -3631,6 +3688,7 @@ getTables(int *numTables) +@@ -3632,6 +3689,7 @@ getTables(int *numTables) "relchecks, (reltriggers <> 0) AS relhastriggers, " "relhasindex, relhasrules, " "'t'::bool AS relhasoids, " @@ -16971,7 +16764,7 @@ index 2431d71..e7ef296 100644 "0 AS relfrozenxid, " "NULL AS reloftype, " "NULL::oid AS owning_tab, " -@@ -3638,6 +3696,7 @@ getTables(int *numTables) +@@ -3639,6 +3697,7 @@ getTables(int *numTables) "NULL AS reltablespace, " "NULL AS reloptions, " "NULL AS toast_reloptions " @@ -16979,7 +16772,7 @@ index 2431d71..e7ef296 100644 "FROM pg_class " "WHERE relkind IN ('%c', '%c', '%c') " "ORDER BY oid", -@@ -3671,6 +3730,7 @@ getTables(int *numTables) +@@ -3672,6 +3731,7 @@ getTables(int *numTables) "NULL AS reltablespace, " "NULL AS reloptions, " "NULL AS toast_reloptions " @@ -16987,7 +16780,7 @@ index 2431d71..e7ef296 100644 "FROM pg_class c " "WHERE relkind IN ('%c', '%c') " "ORDER BY oid", -@@ -3709,6 +3769,7 @@ getTables(int *numTables) +@@ -3710,6 +3770,7 @@ getTables(int *numTables) i_relhasindex = PQfnumber(res, "relhasindex"); i_relhasrules = PQfnumber(res, "relhasrules"); i_relhasoids = PQfnumber(res, "relhasoids"); @@ -16995,7 +16788,7 @@ index 2431d71..e7ef296 100644 i_relfrozenxid = PQfnumber(res, "relfrozenxid"); i_owning_tab = PQfnumber(res, "owning_tab"); i_owning_col = PQfnumber(res, "owning_col"); -@@ -3716,6 +3777,7 @@ getTables(int *numTables) +@@ -3717,6 +3778,7 @@ getTables(int *numTables) i_reloptions = PQfnumber(res, "reloptions"); i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); @@ -17003,7 +16796,7 @@ index 2431d71..e7ef296 100644 if (lockWaitTimeout && g_fout->remoteVersion >= 70300) { -@@ -3748,6 +3810,7 @@ getTables(int *numTables) +@@ -3749,6 +3811,7 @@ getTables(int *numTables) tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0); tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0); tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0); @@ -17011,7 +16804,7 @@ index 2431d71..e7ef296 100644 tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid)); if (PQgetisnull(res, i, i_reloftype)) tblinfo[i].reloftype = NULL; -@@ -3767,6 +3830,7 @@ getTables(int *numTables) +@@ -3768,6 +3831,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)); @@ -17019,7 +16812,7 @@ index 2431d71..e7ef296 100644 /* other fields were zeroed above */ -@@ -5082,6 +5146,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) +@@ -5083,6 +5147,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) int i_attalign; int i_attislocal; int i_attoptions; @@ -17027,7 +16820,7 @@ index 2431d71..e7ef296 100644 PGresult *res; int ntups; bool hasdefaults; -@@ -5128,11 +5193,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables) +@@ -5129,11 +5194,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "a.attlen, a.attalign, a.attislocal, " "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " "array_to_string(attoptions, ', ') AS attoptions " @@ -17041,7 +16834,7 @@ index 2431d71..e7ef296 100644 tbinfo->dobj.catId.oid); } else if (g_fout->remoteVersion >= 70300) -@@ -5144,6 +5211,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) +@@ -5145,6 +5212,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "a.attlen, a.attalign, a.attislocal, " "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " "'' AS attoptions " @@ -17049,7 +16842,7 @@ index 2431d71..e7ef296 100644 "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " "ON a.atttypid = t.oid " "WHERE a.attrelid = '%u'::pg_catalog.oid " -@@ -5165,6 +5233,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) +@@ -5166,6 +5234,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "a.attalign, false AS attislocal, " "format_type(t.oid,a.atttypmod) AS atttypname, " "'' AS attoptions " @@ -17057,7 +16850,7 @@ index 2431d71..e7ef296 100644 "FROM pg_attribute a LEFT JOIN pg_type t " "ON a.atttypid = t.oid " "WHERE a.attrelid = '%u'::oid " -@@ -5183,6 +5252,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) +@@ -5184,6 +5253,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "false AS attislocal, " "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, " "'' AS attoptions " @@ -17065,7 +16858,7 @@ index 2431d71..e7ef296 100644 "FROM pg_attribute a " "WHERE attrelid = '%u'::oid " "AND attnum > 0::int2 " -@@ -5209,6 +5279,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) +@@ -5210,6 +5280,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) i_attalign = PQfnumber(res, "attalign"); i_attislocal = PQfnumber(res, "attislocal"); i_attoptions = PQfnumber(res, "attoptions"); @@ -17073,7 +16866,7 @@ index 2431d71..e7ef296 100644 tbinfo->numatts = ntups; tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); -@@ -5227,6 +5298,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) +@@ -5228,6 +5299,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool)); tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool)); @@ -17081,7 +16874,7 @@ index 2431d71..e7ef296 100644 hasdefaults = false; for (j = 0; j < ntups; j++) -@@ -5256,6 +5328,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) +@@ -5257,6 +5329,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) tbinfo->inhAttrs[j] = false; tbinfo->inhAttrDef[j] = false; tbinfo->inhNotNull[j] = false; @@ -17089,7 +16882,7 @@ index 2431d71..e7ef296 100644 } PQclear(res); -@@ -6146,7 +6219,7 @@ dumpComment(Archive *fout, const char *target, +@@ -6147,7 +6220,7 @@ dumpComment(Archive *fout, const char *target, */ ArchiveEntry(fout, nilCatalogId, createDumpId(), target, namespace, NULL, owner, @@ -17098,7 +16891,7 @@ index 2431d71..e7ef296 100644 query->data, "", NULL, &(dumpId), 1, NULL, NULL); -@@ -6207,7 +6280,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, +@@ -6208,7 +6281,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, target->data, tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -17107,7 +16900,7 @@ index 2431d71..e7ef296 100644 query->data, "", NULL, &(tbinfo->dobj.dumpId), 1, NULL, NULL); -@@ -6229,7 +6302,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, +@@ -6230,7 +6303,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, target->data, tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -17116,7 +16909,7 @@ index 2431d71..e7ef296 100644 query->data, "", NULL, &(tbinfo->dobj.dumpId), 1, NULL, NULL); -@@ -6509,7 +6582,7 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) +@@ -6510,7 +6583,7 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) case DO_BLOB_DATA: ArchiveEntry(fout, dobj->catId, dobj->dumpId, dobj->name, NULL, NULL, "", @@ -17125,7 +16918,7 @@ index 2431d71..e7ef296 100644 "", "", NULL, dobj->dependencies, dobj->nDeps, dumpBlobs, NULL); -@@ -6549,7 +6622,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) +@@ -6550,7 +6623,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) nspinfo->dobj.name, NULL, NULL, nspinfo->rolname, @@ -17134,7 +16927,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, nspinfo->dobj.dependencies, nspinfo->dobj.nDeps, NULL, NULL); -@@ -6561,6 +6634,9 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) +@@ -6562,6 +6635,9 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) NULL, nspinfo->rolname, nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); @@ -17144,7 +16937,7 @@ index 2431d71..e7ef296 100644 dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA", qnspname, NULL, nspinfo->dobj.name, NULL, nspinfo->rolname, nspinfo->nspacl); -@@ -6678,7 +6754,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) +@@ -6679,7 +6755,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, @@ -17153,7 +16946,7 @@ index 2431d71..e7ef296 100644 "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, -@@ -6692,6 +6768,12 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) +@@ -6693,6 +6769,12 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); @@ -17166,7 +16959,7 @@ index 2431d71..e7ef296 100644 PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); -@@ -7054,7 +7136,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) +@@ -7055,7 +7137,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, @@ -17175,7 +16968,7 @@ index 2431d71..e7ef296 100644 "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, -@@ -7068,6 +7150,12 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) +@@ -7069,6 +7151,12 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); @@ -17188,7 +16981,7 @@ index 2431d71..e7ef296 100644 PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); -@@ -7178,7 +7266,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) +@@ -7179,7 +7267,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, @@ -17197,7 +16990,7 @@ index 2431d71..e7ef296 100644 "DOMAIN", SECTION_PRE_DATA, q->data, delq->data, NULL, tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, -@@ -7192,6 +7280,12 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) +@@ -7193,6 +7281,12 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); @@ -17210,7 +17003,7 @@ index 2431d71..e7ef296 100644 destroyPQExpBuffer(q); destroyPQExpBuffer(delq); destroyPQExpBuffer(query); -@@ -7283,7 +7377,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) +@@ -7284,7 +7378,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, @@ -17219,7 +17012,7 @@ index 2431d71..e7ef296 100644 "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, -@@ -7298,6 +7392,12 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) +@@ -7299,6 +7393,12 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); @@ -17232,7 +17025,7 @@ index 2431d71..e7ef296 100644 PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); -@@ -7402,7 +7502,7 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo) +@@ -7403,7 +7503,7 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo) target->data, tyinfo->dobj.namespace->dobj.name, NULL, tyinfo->rolname, @@ -17241,7 +17034,7 @@ index 2431d71..e7ef296 100644 query->data, "", NULL, &(tyinfo->dobj.dumpId), 1, NULL, NULL); -@@ -7454,7 +7554,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo) +@@ -7455,7 +7555,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo) stinfo->dobj.name, stinfo->dobj.namespace->dobj.name, NULL, @@ -17250,7 +17043,7 @@ index 2431d71..e7ef296 100644 "SHELL TYPE", SECTION_PRE_DATA, q->data, "", NULL, stinfo->dobj.dependencies, stinfo->dobj.nDeps, -@@ -7609,7 +7709,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) +@@ -7610,7 +7710,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId, plang->dobj.name, lanschema, NULL, plang->lanowner, @@ -17259,7 +17052,7 @@ index 2431d71..e7ef296 100644 defqry->data, delqry->data, NULL, plang->dobj.dependencies, plang->dobj.nDeps, NULL, NULL); -@@ -7795,6 +7895,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7796,6 +7896,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) char **argmodes = NULL; char **argnames = NULL; char **configitems = NULL; @@ -17267,7 +17060,7 @@ index 2431d71..e7ef296 100644 int nconfigitems = 0; int i; -@@ -7825,8 +7926,10 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7826,8 +7927,10 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "proiswindow, provolatile, proisstrict, prosecdef, " "proconfig, procost, prorows, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " @@ -17278,7 +17071,7 @@ index 2431d71..e7ef296 100644 finfo->dobj.catId.oid); } else if (g_fout->remoteVersion >= 80300) -@@ -7838,6 +7941,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7839,6 +7942,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "provolatile, proisstrict, prosecdef, " "proconfig, procost, prorows, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " @@ -17286,7 +17079,7 @@ index 2431d71..e7ef296 100644 "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); -@@ -7851,6 +7955,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7852,6 +7956,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "provolatile, proisstrict, prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " @@ -17294,7 +17087,7 @@ index 2431d71..e7ef296 100644 "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); -@@ -7866,6 +7971,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7867,6 +7972,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "provolatile, proisstrict, prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " @@ -17302,7 +17095,7 @@ index 2431d71..e7ef296 100644 "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); -@@ -7881,6 +7987,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7882,6 +7988,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "provolatile, proisstrict, prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " @@ -17310,7 +17103,7 @@ index 2431d71..e7ef296 100644 "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); -@@ -7898,6 +8005,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7899,6 +8006,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "false AS prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " @@ -17318,7 +17111,7 @@ index 2431d71..e7ef296 100644 "FROM pg_proc " "WHERE oid = '%u'::oid", finfo->dobj.catId.oid); -@@ -7915,6 +8023,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7916,6 +8024,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "false AS prosecdef, " "NULL AS proconfig, 0 AS procost, 0 AS prorows, " "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " @@ -17326,7 +17119,7 @@ index 2431d71..e7ef296 100644 "FROM pg_proc " "WHERE oid = '%u'::oid", finfo->dobj.catId.oid); -@@ -7959,6 +8068,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7960,6 +8069,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) procost = PQgetvalue(res, 0, PQfnumber(res, "procost")); prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); @@ -17334,7 +17127,7 @@ index 2431d71..e7ef296 100644 /* * See backend/commands/functioncmds.c for details of how the 'AS' clause -@@ -8169,7 +8279,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -8170,7 +8280,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) funcsig_tag, finfo->dobj.namespace->dobj.name, NULL, @@ -17343,7 +17136,7 @@ index 2431d71..e7ef296 100644 "FUNCTION", SECTION_PRE_DATA, q->data, delqry->data, NULL, finfo->dobj.dependencies, finfo->dobj.nDeps, -@@ -8182,6 +8292,12 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -8183,6 +8293,12 @@ dumpFunc(Archive *fout, FuncInfo *finfo) finfo->dobj.namespace->dobj.name, finfo->rolname, finfo->dobj.catId, 0, finfo->dobj.dumpId); @@ -17356,7 +17149,7 @@ index 2431d71..e7ef296 100644 dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION", funcsig, NULL, funcsig_tag, finfo->dobj.namespace->dobj.name, -@@ -8323,7 +8439,7 @@ dumpCast(Archive *fout, CastInfo *cast) +@@ -8324,7 +8440,7 @@ dumpCast(Archive *fout, CastInfo *cast) ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId, castsig->data, "pg_catalog", NULL, "", @@ -17365,7 +17158,7 @@ index 2431d71..e7ef296 100644 defqry->data, delqry->data, NULL, cast->dobj.dependencies, cast->dobj.nDeps, NULL, NULL); -@@ -8567,7 +8683,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) +@@ -8568,7 +8684,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) oprinfo->dobj.namespace->dobj.name, NULL, oprinfo->rolname, @@ -17374,7 +17167,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, oprinfo->dobj.dependencies, oprinfo->dobj.nDeps, NULL, NULL); -@@ -9026,7 +9142,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) +@@ -9027,7 +9143,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) opcinfo->dobj.namespace->dobj.name, NULL, opcinfo->rolname, @@ -17383,7 +17176,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, opcinfo->dobj.dependencies, opcinfo->dobj.nDeps, NULL, NULL); -@@ -9307,7 +9423,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) +@@ -9308,7 +9424,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) opfinfo->dobj.namespace->dobj.name, NULL, opfinfo->rolname, @@ -17392,7 +17185,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, opfinfo->dobj.dependencies, opfinfo->dobj.nDeps, NULL, NULL); -@@ -9423,7 +9539,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) +@@ -9424,7 +9540,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) convinfo->dobj.namespace->dobj.name, NULL, convinfo->rolname, @@ -17401,7 +17194,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, convinfo->dobj.dependencies, convinfo->dobj.nDeps, NULL, NULL); -@@ -9504,11 +9620,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9505,11 +9621,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo) int i_aggtranstype; int i_agginitval; int i_convertok; @@ -17415,7 +17208,7 @@ index 2431d71..e7ef296 100644 bool convertok; /* Skip if not to be dumped */ -@@ -9531,9 +9649,11 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9532,9 +9650,11 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "aggsortop::pg_catalog.regoperator, " "agginitval, " "'t'::boolean AS convertok " @@ -17427,7 +17220,7 @@ index 2431d71..e7ef296 100644 agginfo->aggfn.dobj.catId.oid); } else if (g_fout->remoteVersion >= 70300) -@@ -9543,6 +9663,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9544,6 +9664,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "0 AS aggsortop, " "agginitval, " "'t'::boolean AS convertok " @@ -17435,7 +17228,7 @@ index 2431d71..e7ef296 100644 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " "WHERE a.aggfnoid = p.oid " "AND p.oid = '%u'::pg_catalog.oid", -@@ -9555,6 +9676,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9556,6 +9677,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "0 AS aggsortop, " "agginitval, " "'t'::boolean AS convertok " @@ -17443,7 +17236,7 @@ index 2431d71..e7ef296 100644 "FROM pg_aggregate " "WHERE oid = '%u'::oid", agginfo->aggfn.dobj.catId.oid); -@@ -9567,6 +9689,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9568,6 +9690,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "0 AS aggsortop, " "agginitval1 AS agginitval, " "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok " @@ -17451,7 +17244,7 @@ index 2431d71..e7ef296 100644 "FROM pg_aggregate " "WHERE oid = '%u'::oid", agginfo->aggfn.dobj.catId.oid); -@@ -9592,6 +9715,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9593,6 +9716,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) i_aggtranstype = PQfnumber(res, "aggtranstype"); i_agginitval = PQfnumber(res, "agginitval"); i_convertok = PQfnumber(res, "convertok"); @@ -17459,7 +17252,7 @@ index 2431d71..e7ef296 100644 aggtransfn = PQgetvalue(res, 0, i_aggtransfn); aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn); -@@ -9599,6 +9723,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9600,6 +9724,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) aggtranstype = PQgetvalue(res, 0, i_aggtranstype); agginitval = PQgetvalue(res, 0, i_agginitval); convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't'); @@ -17467,7 +17260,7 @@ index 2431d71..e7ef296 100644 aggsig = format_aggregate_signature(agginfo, fout, true); aggsig_tag = format_aggregate_signature(agginfo, fout, false); -@@ -9667,7 +9792,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9668,7 +9793,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) agginfo->aggfn.dobj.namespace->dobj.name, NULL, agginfo->aggfn.rolname, @@ -17476,7 +17269,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, agginfo->aggfn.dobj.dependencies, agginfo->aggfn.dobj.nDeps, NULL, NULL); -@@ -9679,6 +9804,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9680,6 +9805,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo) agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname, agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId); @@ -17489,7 +17282,7 @@ index 2431d71..e7ef296 100644 /* * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL * command look like a function's GRANT; in particular this affects the -@@ -9755,7 +9886,7 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) +@@ -9756,7 +9887,7 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) prsinfo->dobj.namespace->dobj.name, NULL, "", @@ -17498,7 +17291,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, prsinfo->dobj.dependencies, prsinfo->dobj.nDeps, NULL, NULL); -@@ -9847,7 +9978,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) +@@ -9848,7 +9979,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) dictinfo->dobj.namespace->dobj.name, NULL, dictinfo->rolname, @@ -17507,7 +17300,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, dictinfo->dobj.dependencies, dictinfo->dobj.nDeps, NULL, NULL); -@@ -9907,7 +10038,7 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) +@@ -9908,7 +10039,7 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) tmplinfo->dobj.namespace->dobj.name, NULL, "", @@ -17516,7 +17309,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, tmplinfo->dobj.dependencies, tmplinfo->dobj.nDeps, NULL, NULL); -@@ -10040,7 +10171,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) +@@ -10041,7 +10172,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) cfginfo->dobj.namespace->dobj.name, NULL, cfginfo->rolname, @@ -17525,7 +17318,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, cfginfo->dobj.dependencies, cfginfo->dobj.nDeps, NULL, NULL); -@@ -10096,7 +10227,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) +@@ -10097,7 +10228,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) NULL, NULL, fdwinfo->rolname, @@ -17534,7 +17327,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, fdwinfo->dobj.dependencies, fdwinfo->dobj.nDeps, NULL, NULL); -@@ -10183,7 +10314,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) +@@ -10185,7 +10316,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) NULL, NULL, srvinfo->rolname, @@ -17543,7 +17336,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, srvinfo->dobj.dependencies, srvinfo->dobj.nDeps, NULL, NULL); -@@ -10279,7 +10410,7 @@ dumpUserMappings(Archive *fout, const char *target, +@@ -10290,7 +10421,7 @@ dumpUserMappings(Archive *fout, tag->data, namespace, NULL, @@ -17552,7 +17345,7 @@ index 2431d71..e7ef296 100644 "USER MAPPING", SECTION_PRE_DATA, q->data, delq->data, NULL, &dumpId, 1, -@@ -10350,7 +10481,7 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) +@@ -10361,7 +10492,7 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL, NULL, daclinfo->defaclrole, @@ -17561,7 +17354,7 @@ index 2431d71..e7ef296 100644 q->data, "", NULL, daclinfo->dobj.dependencies, daclinfo->dobj.nDeps, NULL, NULL); -@@ -10407,7 +10538,7 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, +@@ -10418,7 +10549,7 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, tag, nspname, NULL, owner ? owner : "", @@ -17570,7 +17363,7 @@ index 2431d71..e7ef296 100644 sql->data, "", NULL, &(objDumpId), 1, NULL, NULL); -@@ -10416,6 +10547,123 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, +@@ -10427,6 +10558,123 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, } /* @@ -17694,7 +17487,7 @@ index 2431d71..e7ef296 100644 * dumpTable * write out to fout the declarations (not data) of a user-defined table */ -@@ -10571,7 +10819,6 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) +@@ -10582,7 +10830,6 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBuffer(q, "CREATE VIEW %s AS\n %s\n", fmtId(tbinfo->dobj.name), viewdef); @@ -17702,7 +17495,7 @@ index 2431d71..e7ef296 100644 PQclear(res); } else -@@ -10922,6 +11169,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) +@@ -10933,6 +11180,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace, tbinfo->rolname, (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false, @@ -17710,7 +17503,7 @@ index 2431d71..e7ef296 100644 reltypename, SECTION_PRE_DATA, q->data, delq->data, NULL, tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, -@@ -10942,6 +11190,27 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) +@@ -10953,6 +11201,27 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) dumpTableConstraintComment(fout, constr); } @@ -17738,7 +17531,7 @@ index 2431d71..e7ef296 100644 destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); -@@ -10990,7 +11259,7 @@ dumpAttrDef(Archive *fout, AttrDefInfo *adinfo) +@@ -11001,7 +11270,7 @@ dumpAttrDef(Archive *fout, AttrDefInfo *adinfo) tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -17747,7 +17540,7 @@ index 2431d71..e7ef296 100644 q->data, delq->data, NULL, adinfo->dobj.dependencies, adinfo->dobj.nDeps, NULL, NULL); -@@ -11086,7 +11355,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) +@@ -11097,7 +11366,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) indxinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, indxinfo->tablespace, @@ -17756,7 +17549,7 @@ index 2431d71..e7ef296 100644 "INDEX", SECTION_POST_DATA, q->data, delq->data, NULL, indxinfo->dobj.dependencies, indxinfo->dobj.nDeps, -@@ -11211,7 +11480,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) +@@ -11222,7 +11491,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) coninfo->dobj.name, tbinfo->dobj.namespace->dobj.name, indxinfo->tablespace, @@ -17765,7 +17558,7 @@ index 2431d71..e7ef296 100644 "CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, coninfo->dobj.dependencies, coninfo->dobj.nDeps, -@@ -11244,7 +11513,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) +@@ -11255,7 +11524,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) coninfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, @@ -17774,7 +17567,7 @@ index 2431d71..e7ef296 100644 "FK CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, coninfo->dobj.dependencies, coninfo->dobj.nDeps, -@@ -11279,7 +11548,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) +@@ -11290,7 +11559,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) coninfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, @@ -17783,7 +17576,7 @@ index 2431d71..e7ef296 100644 "CHECK CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, coninfo->dobj.dependencies, coninfo->dobj.nDeps, -@@ -11315,7 +11584,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) +@@ -11326,7 +11595,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) coninfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, @@ -17792,7 +17585,7 @@ index 2431d71..e7ef296 100644 "CHECK CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, coninfo->dobj.dependencies, coninfo->dobj.nDeps, -@@ -11601,7 +11870,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) +@@ -11612,7 +11881,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -17801,7 +17594,7 @@ index 2431d71..e7ef296 100644 query->data, delqry->data, NULL, tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, NULL, NULL); -@@ -11637,7 +11906,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) +@@ -11648,7 +11917,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -17810,7 +17603,7 @@ index 2431d71..e7ef296 100644 query->data, "", NULL, &(tbinfo->dobj.dumpId), 1, NULL, NULL); -@@ -11650,6 +11919,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) +@@ -11661,6 +11930,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) dumpComment(fout, query->data, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); @@ -17823,7 +17616,7 @@ index 2431d71..e7ef296 100644 } if (!schemaOnly) -@@ -11665,7 +11940,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) +@@ -11676,7 +11951,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -17832,7 +17625,7 @@ index 2431d71..e7ef296 100644 query->data, "", NULL, &(tbinfo->dobj.dumpId), 1, NULL, NULL); -@@ -11849,7 +12124,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) +@@ -11860,7 +12135,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) tginfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, @@ -17841,7 +17634,7 @@ index 2431d71..e7ef296 100644 "TRIGGER", SECTION_POST_DATA, query->data, delqry->data, NULL, tginfo->dobj.dependencies, tginfo->dobj.nDeps, -@@ -11969,7 +12244,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) +@@ -11980,7 +12255,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) rinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, @@ -17850,7 +17643,7 @@ index 2431d71..e7ef296 100644 "RULE", SECTION_POST_DATA, cmd->data, delcmd->data, NULL, rinfo->dobj.dependencies, rinfo->dobj.nDeps, -@@ -12334,7 +12609,7 @@ fmtQualifiedId(const char *schema, const char *id) +@@ -12345,7 +12620,7 @@ fmtQualifiedId(const char *schema, const char *id) * "", not an invalid "()" column list. */ static const char * @@ -17859,7 +17652,7 @@ index 2431d71..e7ef296 100644 { static PQExpBuffer q = NULL; int numatts = ti->numatts; -@@ -12350,6 +12625,11 @@ fmtCopyColumnList(const TableInfo *ti) +@@ -12361,6 +12636,11 @@ fmtCopyColumnList(const TableInfo *ti) appendPQExpBuffer(q, "("); needComma = false; @@ -17872,7 +17665,7 @@ index 2431d71..e7ef296 100644 { if (attisdropped[i]) diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h -index c93bada..78a50bd 100644 +index c309f69..2a35392 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -137,6 +137,7 @@ typedef struct _namespaceInfo @@ -17928,7 +17721,7 @@ index c93bada..78a50bd 100644 /* global decls */ diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c -index 0c3f63f..83f1678 100644 +index 85032a9..3e4aad6 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -69,6 +69,7 @@ static int disable_triggers = 0; @@ -17956,16 +17749,16 @@ index 0c3f63f..83f1678 100644 else { fprintf(stderr, -@@ -330,6 +334,8 @@ main(int argc, char *argv[]) +@@ -368,6 +372,8 @@ main(int argc, char *argv[]) appendPQExpBuffer(pgdumpopts, " --no-tablespaces"); if (use_setsessauth) appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); + if (security_label) + appendPQExpBuffer(pgdumpopts, " --security-label"); - if (optind < argc) - { -@@ -561,6 +567,7 @@ help(void) + /* + * If there was a database specified on the command line, use that, +@@ -562,6 +568,7 @@ help(void) printf(_(" --use-set-session-authorization\n" " use SET SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n")); @@ -17973,7 +17766,7 @@ index 0c3f63f..83f1678 100644 printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); -@@ -949,6 +956,7 @@ dropTablespaces(PGconn *conn) +@@ -950,6 +957,7 @@ dropTablespaces(PGconn *conn) static void dumpTablespaces(PGconn *conn) { @@ -17981,7 +17774,7 @@ index 0c3f63f..83f1678 100644 PGresult *res; int i; -@@ -957,31 +965,37 @@ dumpTablespaces(PGconn *conn) +@@ -958,31 +966,37 @@ dumpTablespaces(PGconn *conn) * pg_xxx) */ if (server_version >= 90000) @@ -18023,7 +17816,7 @@ index 0c3f63f..83f1678 100644 if (PQntuples(res) > 0) fprintf(OPF, "--\n-- Tablespaces\n--\n\n"); -@@ -994,6 +1008,7 @@ dumpTablespaces(PGconn *conn) +@@ -995,6 +1009,7 @@ dumpTablespaces(PGconn *conn) char *spcacl = PQgetvalue(res, i, 3); char *spcoptions = PQgetvalue(res, i, 4); char *spccomment = PQgetvalue(res, i, 5); @@ -18031,7 +17824,7 @@ index 0c3f63f..83f1678 100644 char *fspcname; /* needed for buildACLCommands() */ -@@ -1010,6 +1025,10 @@ dumpTablespaces(PGconn *conn) +@@ -1011,6 +1026,10 @@ dumpTablespaces(PGconn *conn) appendPQExpBuffer(buf, "ALTER TABLESPACE %s SET (%s);\n", fspcname, spcoptions); @@ -18042,7 +17835,7 @@ index 0c3f63f..83f1678 100644 if (!skip_acls && !buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner, "", server_version, buf)) -@@ -1153,48 +1172,48 @@ dumpCreateDB(PGconn *conn) +@@ -1154,48 +1173,48 @@ dumpCreateDB(PGconn *conn) /* Now collect all the information about databases to dump */ if (server_version >= 80400) @@ -18102,7 +17895,7 @@ index 0c3f63f..83f1678 100644 "coalesce(" "(select usename from pg_shadow where usesysid=datdba), " "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " -@@ -1202,6 +1221,7 @@ dumpCreateDB(PGconn *conn) +@@ -1203,6 +1222,7 @@ dumpCreateDB(PGconn *conn) "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " "datistemplate, '' as datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " @@ -18110,7 +17903,7 @@ index 0c3f63f..83f1678 100644 "FROM pg_database d " "WHERE datallowconn ORDER BY 1"); else -@@ -1210,18 +1230,20 @@ dumpCreateDB(PGconn *conn) +@@ -1211,18 +1231,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. */ @@ -18133,7 +17926,7 @@ index 0c3f63f..83f1678 100644 for (i = 0; i < PQntuples(res); i++) { char *dbname = PQgetvalue(res, i, 0); -@@ -1234,6 +1256,7 @@ dumpCreateDB(PGconn *conn) +@@ -1235,6 +1257,7 @@ dumpCreateDB(PGconn *conn) char *dbacl = PQgetvalue(res, i, 7); char *dbconnlimit = PQgetvalue(res, i, 8); char *dbtablespace = PQgetvalue(res, i, 9); @@ -18141,7 +17934,7 @@ index 0c3f63f..83f1678 100644 char *fdbname; fdbname = strdup(fmtId(dbname)); -@@ -1309,6 +1332,9 @@ dumpCreateDB(PGconn *conn) +@@ -1310,6 +1333,9 @@ dumpCreateDB(PGconn *conn) appendPQExpBuffer(buf, ";\n"); } } @@ -18152,7 +17945,7 @@ index 0c3f63f..83f1678 100644 if (!skip_acls && !buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner, diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c -index b0bcbc2..cdb2592 100644 +index dbdf7ac..03b9d67 100644 --- a/src/bin/pg_dump/pg_restore.c +++ b/src/bin/pg_dump/pg_restore.c @@ -76,6 +76,7 @@ main(int argc, char **argv) @@ -18180,7 +17973,7 @@ index b0bcbc2..cdb2592 100644 else { fprintf(stderr, -@@ -326,6 +330,7 @@ main(int argc, char **argv) +@@ -337,6 +341,7 @@ main(int argc, char **argv) opts->noDataForFailedTables = no_data_for_failed_tables; opts->noTablespace = outputNoTablespaces; opts->use_setsessauth = use_setsessauth; @@ -18188,7 +17981,7 @@ index b0bcbc2..cdb2592 100644 if (opts->formatName) { -@@ -437,6 +442,7 @@ usage(const char *progname) +@@ -448,6 +453,7 @@ usage(const char *progname) " ALTER OWNER commands to set ownership\n")); printf(_(" -1, --single-transaction\n" " restore as a single transaction\n")); @@ -18197,7 +17990,7 @@ index b0bcbc2..cdb2592 100644 printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); diff --git a/src/include/access/htup.h b/src/include/access/htup.h -index d87e888..270ff67 100644 +index 3be701b..4501289 100644 --- a/src/include/access/htup.h +++ b/src/include/access/htup.h @@ -163,7 +163,7 @@ typedef HeapTupleHeaderData *HeapTupleHeader; @@ -18270,7 +18063,7 @@ index d87e888..270ff67 100644 /* * WAL record definitions for heapam.c's WAL operations diff --git a/src/include/access/sysattr.h b/src/include/access/sysattr.h -index 59cd2cd..ad5903b 100644 +index 6047735..ce9a994 100644 --- a/src/include/access/sysattr.h +++ b/src/include/access/sysattr.h @@ -25,7 +25,8 @@ @@ -18284,7 +18077,7 @@ index 59cd2cd..ad5903b 100644 #endif /* SYSATTR_H */ diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h -index 53e0be6..dea713b 100644 +index e38a6e76..d5688d5 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -75,13 +75,14 @@ typedef struct tupleDesc @@ -18305,7 +18098,7 @@ index 53e0be6..dea713b 100644 extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc); diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h -index 49a32e3..f088876 100644 +index 5e989ef..c3ed1d5 100644 --- a/src/include/bootstrap/bootstrap.h +++ b/src/include/bootstrap/bootstrap.h @@ -24,6 +24,7 @@ typedef enum @@ -18317,7 +18110,7 @@ index 49a32e3..f088876 100644 NUM_AUXPROCTYPES /* Must be last! */ } AuxProcType; diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h -index 8292273..e2a86e8 100644 +index 557c311..162ad1c 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -61,7 +61,8 @@ extern Oid heap_create_with_catalog(const char *relname, @@ -18365,7 +18158,7 @@ index 8292273..e2a86e8 100644 extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind, bool allow_system_table_mods); diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h -index 4f437fd..069256f 100644 +index 752a35e..a3da55e 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -255,6 +255,11 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index, 2703, on pg_type using btree(oid oid_ops @@ -18381,7 +18174,7 @@ index 4f437fd..069256f 100644 #define ForeignDataWrapperOidIndexId 112 diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h -index 5ea514d..0f07482 100644 +index 2296fa2..4a412aa 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -60,6 +60,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO @@ -18444,7 +18237,7 @@ index 5ea514d..0f07482 100644 #define RELKIND_INDEX 'i' /* secondary index */ diff --git a/src/include/catalog/pg_conversion_fn.h b/src/include/catalog/pg_conversion_fn.h -index d40dea6..add5fd9 100644 +index 3086936..47f49e2 100644 --- a/src/include/catalog/pg_conversion_fn.h +++ b/src/include/catalog/pg_conversion_fn.h @@ -17,7 +17,7 @@ @@ -18457,7 +18250,7 @@ index d40dea6..add5fd9 100644 extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); diff --git a/src/include/catalog/pg_largeobject.h b/src/include/catalog/pg_largeobject.h -index c4c4a26..6025f56 100644 +index 78b3119..cf43db1 100644 --- a/src/include/catalog/pg_largeobject.h +++ b/src/include/catalog/pg_largeobject.h @@ -51,9 +51,10 @@ typedef FormData_pg_largeobject *Form_pg_largeobject; @@ -18473,7 +18266,7 @@ index c4c4a26..6025f56 100644 #endif /* PG_LARGEOBJECT_H */ diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h -index 425f039..0f8043d 100644 +index cc576a5..eeb1079 100644 --- a/src/include/catalog/pg_namespace.h +++ b/src/include/catalog/pg_namespace.h @@ -77,6 +77,6 @@ DESCR("standard public schema"); @@ -18485,10 +18278,10 @@ index 425f039..0f8043d 100644 #endif /* PG_NAMESPACE_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h -index f2751a4..3e024b3 100644 +index 31a9650..254e19b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h -@@ -3719,6 +3719,10 @@ DESCR("current user privilege on role by role name"); +@@ -3715,6 +3715,10 @@ DESCR("current user privilege on role by role name"); DATA(insert OID = 2710 ( pg_has_role PGNSP PGUID 12 1 0 0 f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ pg_has_role_id _null_ _null_ _null_ )); DESCR("current user privilege on role by role oid"); @@ -18500,7 +18293,7 @@ index f2751a4..3e024b3 100644 DESCR("bytes required to store the value, perhaps with compression"); DATA(insert OID = 2322 ( pg_tablespace_size PGNSP PGUID 12 1 0 0 f f f t f v 1 0 20 "26" _null_ _null_ _null_ _null_ pg_tablespace_size_oid _null_ _null_ _null_ )); diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h -index c886f81..f2351c1 100644 +index 0cb82b0..44ee57b 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, @@ -18599,7 +18392,7 @@ index 0000000..21b25de + +#endif /* PG_SECLABEL_H */ diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h -index 5cf90af..1ca2dd4 100644 +index baf3012..c344bf8 100644 --- a/src/include/catalog/pg_type_fn.h +++ b/src/include/catalog/pg_type_fn.h @@ -50,7 +50,8 @@ extern Oid TypeCreate(Oid newTypeOid, @@ -18613,7 +18406,7 @@ index 5cf90af..1ca2dd4 100644 extern void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h -index fb6f678..f642ded 100644 +index 056fd19..e99dea7 100644 --- a/src/include/catalog/toasting.h +++ b/src/include/catalog/toasting.h @@ -58,5 +58,8 @@ DECLARE_TOAST(pg_shdescription, 2846, 2847); @@ -18626,7 +18419,7 @@ index fb6f678..f642ded 100644 #endif /* TOASTING_H */ diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h -index b1e04fb..6cd19ba 100644 +index 83e735a..37b772e 100644 --- a/src/include/commands/alter.h +++ b/src/include/commands/alter.h @@ -19,5 +19,6 @@ @@ -18637,7 +18430,7 @@ index b1e04fb..6cd19ba 100644 #endif /* ALTER_H */ diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h -index 542fc27..874e07a 100644 +index f150da3..c085321 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); @@ -18649,7 +18442,7 @@ index 542fc27..874e07a 100644 extern Oid get_database_oid(const char *dbname); extern char *get_database_name(Oid dbid); diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h -index e8dbe81..7fc4922 100644 +index cd5be6e..9a71ae4 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -60,6 +60,8 @@ extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType); @@ -18662,7 +18455,7 @@ index e8dbe81..7fc4922 100644 extern void CreateCast(CreateCastStmt *stmt); extern void DropCast(DropCastStmt *stmt); diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h -index c914bd5..edd5abd 100644 +index 62562fa..d122106 100644 --- a/src/include/commands/schemacmds.h +++ b/src/include/commands/schemacmds.h @@ -26,5 +26,6 @@ extern void RemoveSchemaById(Oid schemaOid); @@ -18673,7 +18466,7 @@ index c914bd5..edd5abd 100644 #endif /* SCHEMACMDS_H */ diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h -index f9269cc..a531f45 100644 +index c1774a2..cc6b11a 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -35,6 +35,13 @@ extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, @@ -18691,7 +18484,7 @@ index f9269cc..a531f45 100644 extern void ExecuteTruncate(TruncateStmt *stmt); diff --git a/src/include/commands/tablespace.h b/src/include/commands/tablespace.h -index cf005ee..e9e8a55 100644 +index 3d46eeb..71fa5d2 100644 --- a/src/include/commands/tablespace.h +++ b/src/include/commands/tablespace.h @@ -44,6 +44,7 @@ extern void DropTableSpace(DropTableSpaceStmt *stmt); @@ -18703,7 +18496,7 @@ index cf005ee..e9e8a55 100644 extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo); diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h -index 8e5b610..f8c5872 100644 +index 4dc2d4f..e50a83f 100644 --- a/src/include/commands/typecmds.h +++ b/src/include/commands/typecmds.h @@ -43,5 +43,7 @@ extern void AlterTypeNamespace(List *names, const char *newschema); @@ -18715,7 +18508,7 @@ index 8e5b610..f8c5872 100644 #endif /* TYPECMDS_H */ diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h -index 820314c..7873b44 100644 +index b2424a0..609f31b 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -131,8 +131,8 @@ extern TupleHashEntry FindTupleHashEntry(TupleHashTable hashtable, @@ -18749,7 +18542,7 @@ index 820314c..7873b44 100644 extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); diff --git a/src/include/fmgr.h b/src/include/fmgr.h -index d1a0dc1..e9de809 100644 +index e588bb1..1298e3e 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -51,6 +51,7 @@ typedef struct FmgrInfo @@ -18761,7 +18554,7 @@ index d1a0dc1..e9de809 100644 fmNodePtr fn_expr; /* expression parse tree for call, or NULL */ } FmgrInfo; diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h -index 1b5e476..37ffdfa 100644 +index 401b69e..4e2c59d 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -346,6 +346,7 @@ typedef enum NodeTag @@ -18773,7 +18566,7 @@ index 1b5e476..37ffdfa 100644 /* * TAGS FOR PARSE TREE NODES (parsenodes.h) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h -index b591073..53609e8 100644 +index 54eebbc..94d0969 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -731,6 +731,7 @@ typedef struct RangeTblEntry @@ -18793,7 +18586,7 @@ index b591073..53609e8 100644 AT_SetTableSpace, /* SET TABLESPACE */ AT_SetRelOptions, /* SET (...) -- AM specific parameters */ AT_ResetRelOptions, /* RESET (...) -- AM specific parameters */ -@@ -2073,6 +2076,20 @@ typedef struct AlterOwnerStmt +@@ -2074,6 +2077,20 @@ typedef struct AlterOwnerStmt char *newowner; /* the new owner */ } AlterOwnerStmt; @@ -18815,7 +18608,7 @@ index b591073..53609e8 100644 /* ---------------------- * Create Rule Statement diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h -index 49d4b6c..13c88a1 100644 +index 5065bd6..0c67e25 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -208,6 +208,7 @@ PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD) @@ -18827,10 +18620,10 @@ index 49d4b6c..13c88a1 100644 PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD) PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD) diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in -index 684aed4..2080c95 100644 +index fd169b6..4dd3ca7 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in -@@ -412,6 +412,9 @@ +@@ -418,6 +418,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SECURITY_PAM_APPL_H @@ -19464,7 +19257,7 @@ index 0000000..5fd7195 + +#endif /* SEPGSQL_H */ diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h -index 43a61f3..6385b5c 100644 +index b8de372..2bd6973 100644 --- a/src/include/storage/large_object.h +++ b/src/include/storage/large_object.h @@ -70,7 +70,7 @@ typedef struct LargeObjectDesc @@ -19477,7 +19270,7 @@ index 43a61f3..6385b5c 100644 extern void inv_close(LargeObjectDesc *obj_desc); extern int inv_drop(Oid lobjId); diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h -index 4eece8b..4176eac 100644 +index 0322007..f807707 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -70,6 +70,7 @@ typedef enum LWLockId @@ -19489,7 +19282,7 @@ index 4eece8b..4176eac 100644 FirstBufMappingLock, FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h -index c22c65a..5b94283 100644 +index 9eb37b88..6165c4d 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -166,6 +166,7 @@ extern bool log_btree_build_stats; @@ -19501,7 +19294,7 @@ index c22c65a..5b94283 100644 extern int log_min_error_statement; diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h -index 2f19e5c..6744782 100644 +index 9faefbe..90b7c94 100644 --- a/src/include/utils/syscache.h +++ b/src/include/utils/syscache.h @@ -101,6 +101,8 @@ extern bool SearchSysCacheExists(int cacheId, @@ -19530,7 +19323,7 @@ index 2f19e5c..6744782 100644 SearchSysCacheList(cacheId, 1, key1, 0, 0, 0) #define SearchSysCacheList2(cacheId, key1, key2) \ diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c -index 656ea73..2ce4e94 100644 +index 25d2760..9bb9d3f 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -1936,7 +1936,7 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars) From 723418aa9c8887840fecd02d744917c5b81d0790 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Thu, 7 Oct 2010 10:10:50 +0900 Subject: [PATCH 20/24] upgrade base version v9.0.0->v9.0.1 --- .gitignore | 1 + sepostgresql-9.0-fullset.patch | 174 ++++++++++++++++----------------- sepostgresql.spec | 7 +- sources | 2 +- 4 files changed, 94 insertions(+), 90 deletions(-) diff --git a/.gitignore b/.gitignore index 07eb4f3..1b4ef6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ postgresql-9.0beta1.tar.gz /postgresql-9.0.0.tar.gz +/postgresql-9.0.1.tar.gz diff --git a/sepostgresql-9.0-fullset.patch b/sepostgresql-9.0-fullset.patch index 01b861d..f3dddff 100644 --- a/sepostgresql-9.0-fullset.patch +++ b/sepostgresql-9.0-fullset.patch @@ -1,5 +1,5 @@ diff --git a/configure b/configure -index 7c54dc6..bb7e49c 100755 +index c4ba09e..bbed799 100755 --- a/configure +++ b/configure @@ -708,6 +708,7 @@ LDFLAGS_EX @@ -229,7 +229,7 @@ index 7c54dc6..bb7e49c 100755 # diff --git a/configure.in b/configure.in -index d7fec2c..cc3b119 100644 +index c381505..14d0aba 100644 --- a/configure.in +++ b/configure.in @@ -755,6 +755,19 @@ PGAC_ARG_BOOL(with, zlib, yes, @@ -3092,7 +3092,7 @@ index 2e5a7df..9647622 100644 /* * 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 4e95a83..a09eabe 100644 +index a6e1d35..50921ec 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -22,7 +22,10 @@ @@ -7374,10 +7374,10 @@ index 464183d..67d0316 100644 PG_RETURN_INT32(0); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c -index 3e237c1..387d85d 100644 +index 6e6844b..8d58a17 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c -@@ -1824,6 +1824,7 @@ _copyRangeTblEntry(RangeTblEntry *from) +@@ -1825,6 +1825,7 @@ _copyRangeTblEntry(RangeTblEntry *from) COPY_SCALAR_FIELD(checkAsUser); COPY_BITMAPSET_FIELD(selectedCols); COPY_BITMAPSET_FIELD(modifiedCols); @@ -7385,7 +7385,7 @@ index 3e237c1..387d85d 100644 return newnode; } -@@ -2756,6 +2757,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) +@@ -2757,6 +2758,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) return newnode; } @@ -7407,7 +7407,7 @@ index 3e237c1..387d85d 100644 static RuleStmt * _copyRuleStmt(RuleStmt *from) { -@@ -3970,6 +3986,9 @@ copyObject(void *from) +@@ -3971,6 +3987,9 @@ copyObject(void *from) case T_AlterOwnerStmt: retval = _copyAlterOwnerStmt(from); break; @@ -7418,10 +7418,10 @@ index 3e237c1..387d85d 100644 retval = _copyRuleStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c -index dc5f883..33f2f5d 100644 +index 6e00584..2adfa91 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c -@@ -1308,6 +1308,19 @@ _equalAlterOwnerStmt(AlterOwnerStmt *a, AlterOwnerStmt *b) +@@ -1309,6 +1309,19 @@ _equalAlterOwnerStmt(AlterOwnerStmt *a, AlterOwnerStmt *b) } static bool @@ -7441,7 +7441,7 @@ index dc5f883..33f2f5d 100644 _equalRuleStmt(RuleStmt *a, RuleStmt *b) { COMPARE_NODE_FIELD(relation); -@@ -2187,6 +2200,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) +@@ -2188,6 +2201,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(modifiedCols); @@ -7449,7 +7449,7 @@ index dc5f883..33f2f5d 100644 return true; } -@@ -2658,6 +2672,9 @@ equal(void *a, void *b) +@@ -2659,6 +2673,9 @@ equal(void *a, void *b) case T_AlterOwnerStmt: retval = _equalAlterOwnerStmt(a, b); break; @@ -7460,10 +7460,10 @@ index dc5f883..33f2f5d 100644 retval = _equalRuleStmt(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c -index 48c0888..34632d2 100644 +index ae0e2ab..36eee35 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c -@@ -2136,6 +2136,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) +@@ -2137,6 +2137,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) WRITE_OID_FIELD(checkAsUser); WRITE_BITMAPSET_FIELD(selectedCols); WRITE_BITMAPSET_FIELD(modifiedCols); @@ -15191,10 +15191,10 @@ index 79ca6c1..70fc9fb 100644 OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee", diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c -index 8e342a9..2cbd1d9 100644 +index 705fcf1..e897f55 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c -@@ -4200,7 +4200,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS) +@@ -4213,7 +4213,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS) * build tupdesc for result tuples. This must match this function's * pg_proc entry! */ @@ -15203,7 +15203,7 @@ index 8e342a9..2cbd1d9 100644 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "abbrev", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "utc_offset", -@@ -4293,7 +4293,7 @@ pg_timezone_names(PG_FUNCTION_ARGS) +@@ -4306,7 +4306,7 @@ pg_timezone_names(PG_FUNCTION_ARGS) * build tupdesc for result tuples. This must match this function's * pg_proc entry! */ @@ -15983,7 +15983,7 @@ index 4e55b16..a9f9f7e 100644 TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample -index a3b1457..b78c6d9 100644 +index e1f5ab6..43f47f6 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -76,6 +76,8 @@ @@ -16295,7 +16295,7 @@ index 0a135ee..27b2ef5 100644 char *defn; char *dropStmt; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c -index 7a06f9b..f7fa80a 100644 +index f93affd..0a89cf2 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -125,7 +125,7 @@ static int binary_upgrade = 0; @@ -16882,7 +16882,7 @@ index 7a06f9b..f7fa80a 100644 } PQclear(res); -@@ -6147,7 +6220,7 @@ dumpComment(Archive *fout, const char *target, +@@ -6153,7 +6226,7 @@ dumpComment(Archive *fout, const char *target, */ ArchiveEntry(fout, nilCatalogId, createDumpId(), target, namespace, NULL, owner, @@ -16891,7 +16891,7 @@ index 7a06f9b..f7fa80a 100644 query->data, "", NULL, &(dumpId), 1, NULL, NULL); -@@ -6208,7 +6281,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, +@@ -6214,7 +6287,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, target->data, tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -16900,7 +16900,7 @@ index 7a06f9b..f7fa80a 100644 query->data, "", NULL, &(tbinfo->dobj.dumpId), 1, NULL, NULL); -@@ -6230,7 +6303,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, +@@ -6236,7 +6309,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, target->data, tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -16909,7 +16909,7 @@ index 7a06f9b..f7fa80a 100644 query->data, "", NULL, &(tbinfo->dobj.dumpId), 1, NULL, NULL); -@@ -6510,7 +6583,7 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) +@@ -6516,7 +6589,7 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) case DO_BLOB_DATA: ArchiveEntry(fout, dobj->catId, dobj->dumpId, dobj->name, NULL, NULL, "", @@ -16918,7 +16918,7 @@ index 7a06f9b..f7fa80a 100644 "", "", NULL, dobj->dependencies, dobj->nDeps, dumpBlobs, NULL); -@@ -6550,7 +6623,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) +@@ -6556,7 +6629,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) nspinfo->dobj.name, NULL, NULL, nspinfo->rolname, @@ -16927,7 +16927,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, nspinfo->dobj.dependencies, nspinfo->dobj.nDeps, NULL, NULL); -@@ -6562,6 +6635,9 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) +@@ -6568,6 +6641,9 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) NULL, nspinfo->rolname, nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); @@ -16937,7 +16937,7 @@ index 7a06f9b..f7fa80a 100644 dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA", qnspname, NULL, nspinfo->dobj.name, NULL, nspinfo->rolname, nspinfo->nspacl); -@@ -6679,7 +6755,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) +@@ -6685,7 +6761,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, @@ -16946,7 +16946,7 @@ index 7a06f9b..f7fa80a 100644 "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, -@@ -6693,6 +6769,12 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) +@@ -6699,6 +6775,12 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); @@ -16959,7 +16959,7 @@ index 7a06f9b..f7fa80a 100644 PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); -@@ -7055,7 +7137,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) +@@ -7061,7 +7143,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, @@ -16968,7 +16968,7 @@ index 7a06f9b..f7fa80a 100644 "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, -@@ -7069,6 +7151,12 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) +@@ -7075,6 +7157,12 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); @@ -16981,7 +16981,7 @@ index 7a06f9b..f7fa80a 100644 PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); -@@ -7179,7 +7267,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) +@@ -7185,7 +7273,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, @@ -16990,7 +16990,7 @@ index 7a06f9b..f7fa80a 100644 "DOMAIN", SECTION_PRE_DATA, q->data, delq->data, NULL, tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, -@@ -7193,6 +7281,12 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) +@@ -7199,6 +7287,12 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); @@ -17003,7 +17003,7 @@ index 7a06f9b..f7fa80a 100644 destroyPQExpBuffer(q); destroyPQExpBuffer(delq); destroyPQExpBuffer(query); -@@ -7284,7 +7378,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) +@@ -7290,7 +7384,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, @@ -17012,7 +17012,7 @@ index 7a06f9b..f7fa80a 100644 "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, -@@ -7299,6 +7393,12 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) +@@ -7305,6 +7399,12 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); @@ -17025,7 +17025,7 @@ index 7a06f9b..f7fa80a 100644 PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); -@@ -7403,7 +7503,7 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo) +@@ -7409,7 +7509,7 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo) target->data, tyinfo->dobj.namespace->dobj.name, NULL, tyinfo->rolname, @@ -17034,7 +17034,7 @@ index 7a06f9b..f7fa80a 100644 query->data, "", NULL, &(tyinfo->dobj.dumpId), 1, NULL, NULL); -@@ -7455,7 +7555,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo) +@@ -7461,7 +7561,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo) stinfo->dobj.name, stinfo->dobj.namespace->dobj.name, NULL, @@ -17043,7 +17043,7 @@ index 7a06f9b..f7fa80a 100644 "SHELL TYPE", SECTION_PRE_DATA, q->data, "", NULL, stinfo->dobj.dependencies, stinfo->dobj.nDeps, -@@ -7610,7 +7710,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) +@@ -7616,7 +7716,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId, plang->dobj.name, lanschema, NULL, plang->lanowner, @@ -17052,7 +17052,7 @@ index 7a06f9b..f7fa80a 100644 defqry->data, delqry->data, NULL, plang->dobj.dependencies, plang->dobj.nDeps, NULL, NULL); -@@ -7796,6 +7896,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7802,6 +7902,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) char **argmodes = NULL; char **argnames = NULL; char **configitems = NULL; @@ -17060,7 +17060,7 @@ index 7a06f9b..f7fa80a 100644 int nconfigitems = 0; int i; -@@ -7826,8 +7927,10 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7832,8 +7933,10 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "proiswindow, provolatile, proisstrict, prosecdef, " "proconfig, procost, prorows, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " @@ -17071,7 +17071,7 @@ index 7a06f9b..f7fa80a 100644 finfo->dobj.catId.oid); } else if (g_fout->remoteVersion >= 80300) -@@ -7839,6 +7942,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7845,6 +7948,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "provolatile, proisstrict, prosecdef, " "proconfig, procost, prorows, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " @@ -17079,7 +17079,7 @@ index 7a06f9b..f7fa80a 100644 "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); -@@ -7852,6 +7956,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7858,6 +7962,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "provolatile, proisstrict, prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " @@ -17087,7 +17087,7 @@ index 7a06f9b..f7fa80a 100644 "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); -@@ -7867,6 +7972,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7873,6 +7978,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "provolatile, proisstrict, prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " @@ -17095,7 +17095,7 @@ index 7a06f9b..f7fa80a 100644 "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); -@@ -7882,6 +7988,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7888,6 +7994,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "provolatile, proisstrict, prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " @@ -17103,7 +17103,7 @@ index 7a06f9b..f7fa80a 100644 "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); -@@ -7899,6 +8006,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7905,6 +8012,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "false AS prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " @@ -17111,7 +17111,7 @@ index 7a06f9b..f7fa80a 100644 "FROM pg_proc " "WHERE oid = '%u'::oid", finfo->dobj.catId.oid); -@@ -7916,6 +8024,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7922,6 +8030,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "false AS prosecdef, " "NULL AS proconfig, 0 AS procost, 0 AS prorows, " "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " @@ -17119,7 +17119,7 @@ index 7a06f9b..f7fa80a 100644 "FROM pg_proc " "WHERE oid = '%u'::oid", finfo->dobj.catId.oid); -@@ -7960,6 +8069,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -7966,6 +8075,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) procost = PQgetvalue(res, 0, PQfnumber(res, "procost")); prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); @@ -17127,7 +17127,7 @@ index 7a06f9b..f7fa80a 100644 /* * See backend/commands/functioncmds.c for details of how the 'AS' clause -@@ -8170,7 +8280,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -8176,7 +8286,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) funcsig_tag, finfo->dobj.namespace->dobj.name, NULL, @@ -17136,7 +17136,7 @@ index 7a06f9b..f7fa80a 100644 "FUNCTION", SECTION_PRE_DATA, q->data, delqry->data, NULL, finfo->dobj.dependencies, finfo->dobj.nDeps, -@@ -8183,6 +8293,12 @@ dumpFunc(Archive *fout, FuncInfo *finfo) +@@ -8189,6 +8299,12 @@ dumpFunc(Archive *fout, FuncInfo *finfo) finfo->dobj.namespace->dobj.name, finfo->rolname, finfo->dobj.catId, 0, finfo->dobj.dumpId); @@ -17149,7 +17149,7 @@ index 7a06f9b..f7fa80a 100644 dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION", funcsig, NULL, funcsig_tag, finfo->dobj.namespace->dobj.name, -@@ -8324,7 +8440,7 @@ dumpCast(Archive *fout, CastInfo *cast) +@@ -8330,7 +8446,7 @@ dumpCast(Archive *fout, CastInfo *cast) ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId, castsig->data, "pg_catalog", NULL, "", @@ -17158,7 +17158,7 @@ index 7a06f9b..f7fa80a 100644 defqry->data, delqry->data, NULL, cast->dobj.dependencies, cast->dobj.nDeps, NULL, NULL); -@@ -8568,7 +8684,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) +@@ -8574,7 +8690,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) oprinfo->dobj.namespace->dobj.name, NULL, oprinfo->rolname, @@ -17167,7 +17167,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, oprinfo->dobj.dependencies, oprinfo->dobj.nDeps, NULL, NULL); -@@ -9027,7 +9143,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) +@@ -9033,7 +9149,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) opcinfo->dobj.namespace->dobj.name, NULL, opcinfo->rolname, @@ -17176,7 +17176,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, opcinfo->dobj.dependencies, opcinfo->dobj.nDeps, NULL, NULL); -@@ -9308,7 +9424,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) +@@ -9314,7 +9430,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) opfinfo->dobj.namespace->dobj.name, NULL, opfinfo->rolname, @@ -17185,7 +17185,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, opfinfo->dobj.dependencies, opfinfo->dobj.nDeps, NULL, NULL); -@@ -9424,7 +9540,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) +@@ -9430,7 +9546,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) convinfo->dobj.namespace->dobj.name, NULL, convinfo->rolname, @@ -17194,7 +17194,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, convinfo->dobj.dependencies, convinfo->dobj.nDeps, NULL, NULL); -@@ -9505,11 +9621,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9511,11 +9627,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo) int i_aggtranstype; int i_agginitval; int i_convertok; @@ -17208,7 +17208,7 @@ index 7a06f9b..f7fa80a 100644 bool convertok; /* Skip if not to be dumped */ -@@ -9532,9 +9650,11 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9538,9 +9656,11 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "aggsortop::pg_catalog.regoperator, " "agginitval, " "'t'::boolean AS convertok " @@ -17220,7 +17220,7 @@ index 7a06f9b..f7fa80a 100644 agginfo->aggfn.dobj.catId.oid); } else if (g_fout->remoteVersion >= 70300) -@@ -9544,6 +9664,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9550,6 +9670,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "0 AS aggsortop, " "agginitval, " "'t'::boolean AS convertok " @@ -17228,7 +17228,7 @@ index 7a06f9b..f7fa80a 100644 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " "WHERE a.aggfnoid = p.oid " "AND p.oid = '%u'::pg_catalog.oid", -@@ -9556,6 +9677,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9562,6 +9683,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "0 AS aggsortop, " "agginitval, " "'t'::boolean AS convertok " @@ -17236,7 +17236,7 @@ index 7a06f9b..f7fa80a 100644 "FROM pg_aggregate " "WHERE oid = '%u'::oid", agginfo->aggfn.dobj.catId.oid); -@@ -9568,6 +9690,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9574,6 +9696,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "0 AS aggsortop, " "agginitval1 AS agginitval, " "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok " @@ -17244,7 +17244,7 @@ index 7a06f9b..f7fa80a 100644 "FROM pg_aggregate " "WHERE oid = '%u'::oid", agginfo->aggfn.dobj.catId.oid); -@@ -9593,6 +9716,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9599,6 +9722,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) i_aggtranstype = PQfnumber(res, "aggtranstype"); i_agginitval = PQfnumber(res, "agginitval"); i_convertok = PQfnumber(res, "convertok"); @@ -17252,7 +17252,7 @@ index 7a06f9b..f7fa80a 100644 aggtransfn = PQgetvalue(res, 0, i_aggtransfn); aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn); -@@ -9600,6 +9724,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9606,6 +9730,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) aggtranstype = PQgetvalue(res, 0, i_aggtranstype); agginitval = PQgetvalue(res, 0, i_agginitval); convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't'); @@ -17260,7 +17260,7 @@ index 7a06f9b..f7fa80a 100644 aggsig = format_aggregate_signature(agginfo, fout, true); aggsig_tag = format_aggregate_signature(agginfo, fout, false); -@@ -9668,7 +9793,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9674,7 +9799,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) agginfo->aggfn.dobj.namespace->dobj.name, NULL, agginfo->aggfn.rolname, @@ -17269,7 +17269,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, agginfo->aggfn.dobj.dependencies, agginfo->aggfn.dobj.nDeps, NULL, NULL); -@@ -9680,6 +9805,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo) +@@ -9686,6 +9811,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo) agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname, agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId); @@ -17282,7 +17282,7 @@ index 7a06f9b..f7fa80a 100644 /* * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL * command look like a function's GRANT; in particular this affects the -@@ -9756,7 +9887,7 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) +@@ -9762,7 +9893,7 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) prsinfo->dobj.namespace->dobj.name, NULL, "", @@ -17291,7 +17291,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, prsinfo->dobj.dependencies, prsinfo->dobj.nDeps, NULL, NULL); -@@ -9848,7 +9979,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) +@@ -9854,7 +9985,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) dictinfo->dobj.namespace->dobj.name, NULL, dictinfo->rolname, @@ -17300,7 +17300,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, dictinfo->dobj.dependencies, dictinfo->dobj.nDeps, NULL, NULL); -@@ -9908,7 +10039,7 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) +@@ -9914,7 +10045,7 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) tmplinfo->dobj.namespace->dobj.name, NULL, "", @@ -17309,7 +17309,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, tmplinfo->dobj.dependencies, tmplinfo->dobj.nDeps, NULL, NULL); -@@ -10041,7 +10172,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) +@@ -10047,7 +10178,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) cfginfo->dobj.namespace->dobj.name, NULL, cfginfo->rolname, @@ -17318,7 +17318,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, cfginfo->dobj.dependencies, cfginfo->dobj.nDeps, NULL, NULL); -@@ -10097,7 +10228,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) +@@ -10103,7 +10234,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) NULL, NULL, fdwinfo->rolname, @@ -17327,7 +17327,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, fdwinfo->dobj.dependencies, fdwinfo->dobj.nDeps, NULL, NULL); -@@ -10185,7 +10316,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) +@@ -10191,7 +10322,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) NULL, NULL, srvinfo->rolname, @@ -17336,7 +17336,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, srvinfo->dobj.dependencies, srvinfo->dobj.nDeps, NULL, NULL); -@@ -10290,7 +10421,7 @@ dumpUserMappings(Archive *fout, +@@ -10296,7 +10427,7 @@ dumpUserMappings(Archive *fout, tag->data, namespace, NULL, @@ -17345,7 +17345,7 @@ index 7a06f9b..f7fa80a 100644 "USER MAPPING", SECTION_PRE_DATA, q->data, delq->data, NULL, &dumpId, 1, -@@ -10361,7 +10492,7 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) +@@ -10367,7 +10498,7 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL, NULL, daclinfo->defaclrole, @@ -17354,7 +17354,7 @@ index 7a06f9b..f7fa80a 100644 q->data, "", NULL, daclinfo->dobj.dependencies, daclinfo->dobj.nDeps, NULL, NULL); -@@ -10418,7 +10549,7 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, +@@ -10424,7 +10555,7 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, tag, nspname, NULL, owner ? owner : "", @@ -17363,7 +17363,7 @@ index 7a06f9b..f7fa80a 100644 sql->data, "", NULL, &(objDumpId), 1, NULL, NULL); -@@ -10427,6 +10558,123 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, +@@ -10433,6 +10564,123 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, } /* @@ -17487,7 +17487,7 @@ index 7a06f9b..f7fa80a 100644 * dumpTable * write out to fout the declarations (not data) of a user-defined table */ -@@ -10582,7 +10830,6 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) +@@ -10588,7 +10836,6 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBuffer(q, "CREATE VIEW %s AS\n %s\n", fmtId(tbinfo->dobj.name), viewdef); @@ -17495,7 +17495,7 @@ index 7a06f9b..f7fa80a 100644 PQclear(res); } else -@@ -10933,6 +11180,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) +@@ -10939,6 +11186,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace, tbinfo->rolname, (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false, @@ -17503,7 +17503,7 @@ index 7a06f9b..f7fa80a 100644 reltypename, SECTION_PRE_DATA, q->data, delq->data, NULL, tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, -@@ -10953,6 +11201,27 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) +@@ -10959,6 +11207,27 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) dumpTableConstraintComment(fout, constr); } @@ -17531,7 +17531,7 @@ index 7a06f9b..f7fa80a 100644 destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); -@@ -11001,7 +11270,7 @@ dumpAttrDef(Archive *fout, AttrDefInfo *adinfo) +@@ -11007,7 +11276,7 @@ dumpAttrDef(Archive *fout, AttrDefInfo *adinfo) tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -17540,7 +17540,7 @@ index 7a06f9b..f7fa80a 100644 q->data, delq->data, NULL, adinfo->dobj.dependencies, adinfo->dobj.nDeps, NULL, NULL); -@@ -11097,7 +11366,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) +@@ -11103,7 +11372,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) indxinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, indxinfo->tablespace, @@ -17549,7 +17549,7 @@ index 7a06f9b..f7fa80a 100644 "INDEX", SECTION_POST_DATA, q->data, delq->data, NULL, indxinfo->dobj.dependencies, indxinfo->dobj.nDeps, -@@ -11222,7 +11491,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) +@@ -11228,7 +11497,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) coninfo->dobj.name, tbinfo->dobj.namespace->dobj.name, indxinfo->tablespace, @@ -17558,7 +17558,7 @@ index 7a06f9b..f7fa80a 100644 "CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, coninfo->dobj.dependencies, coninfo->dobj.nDeps, -@@ -11255,7 +11524,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) +@@ -11261,7 +11530,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) coninfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, @@ -17567,7 +17567,7 @@ index 7a06f9b..f7fa80a 100644 "FK CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, coninfo->dobj.dependencies, coninfo->dobj.nDeps, -@@ -11290,7 +11559,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) +@@ -11296,7 +11565,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) coninfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, @@ -17576,7 +17576,7 @@ index 7a06f9b..f7fa80a 100644 "CHECK CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, coninfo->dobj.dependencies, coninfo->dobj.nDeps, -@@ -11326,7 +11595,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) +@@ -11332,7 +11601,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) coninfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, @@ -17585,7 +17585,7 @@ index 7a06f9b..f7fa80a 100644 "CHECK CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, coninfo->dobj.dependencies, coninfo->dobj.nDeps, -@@ -11612,7 +11881,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) +@@ -11618,7 +11887,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -17594,7 +17594,7 @@ index 7a06f9b..f7fa80a 100644 query->data, delqry->data, NULL, tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, NULL, NULL); -@@ -11648,7 +11917,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) +@@ -11654,7 +11923,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -17603,7 +17603,7 @@ index 7a06f9b..f7fa80a 100644 query->data, "", NULL, &(tbinfo->dobj.dumpId), 1, NULL, NULL); -@@ -11661,6 +11930,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) +@@ -11667,6 +11936,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) dumpComment(fout, query->data, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); @@ -17616,7 +17616,7 @@ index 7a06f9b..f7fa80a 100644 } if (!schemaOnly) -@@ -11676,7 +11951,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) +@@ -11682,7 +11957,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, @@ -17625,7 +17625,7 @@ index 7a06f9b..f7fa80a 100644 query->data, "", NULL, &(tbinfo->dobj.dumpId), 1, NULL, NULL); -@@ -11860,7 +12135,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) +@@ -11866,7 +12141,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) tginfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, @@ -17634,7 +17634,7 @@ index 7a06f9b..f7fa80a 100644 "TRIGGER", SECTION_POST_DATA, query->data, delqry->data, NULL, tginfo->dobj.dependencies, tginfo->dobj.nDeps, -@@ -11980,7 +12255,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) +@@ -11986,7 +12261,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) rinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, @@ -17643,7 +17643,7 @@ index 7a06f9b..f7fa80a 100644 "RULE", SECTION_POST_DATA, cmd->data, delcmd->data, NULL, rinfo->dobj.dependencies, rinfo->dobj.nDeps, -@@ -12345,7 +12620,7 @@ fmtQualifiedId(const char *schema, const char *id) +@@ -12351,7 +12626,7 @@ fmtQualifiedId(const char *schema, const char *id) * "", not an invalid "()" column list. */ static const char * @@ -17652,7 +17652,7 @@ index 7a06f9b..f7fa80a 100644 { static PQExpBuffer q = NULL; int numatts = ti->numatts; -@@ -12361,6 +12636,11 @@ fmtCopyColumnList(const TableInfo *ti) +@@ -12367,6 +12642,11 @@ fmtCopyColumnList(const TableInfo *ti) appendPQExpBuffer(q, "("); needComma = false; diff --git a/sepostgresql.spec b/sepostgresql.spec index 05ea0e8..b79d3e7 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -11,8 +11,8 @@ Summary: Security Enhanced PostgreSQL Name: sepostgresql -Version: 9.0.0 -Release: 20101005%{?dist} +Version: 9.0.1 +Release: 20101007%{?dist} License: PostgreSQL Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ @@ -181,6 +181,9 @@ fi %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog +* Thu Oct 7 2010 KaiGai Kohei - 9.0.1-20101007 +- upgrade base version to 9.0.1 + * Tue Oct 5 2010 KaiGai Kohei - 9.0.0-20101005 - upgrade base version to 9.0.0 diff --git a/sources b/sources index f048e2f..5a1101a 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -9443b3b9c95a48d5e0713feafc209adc postgresql-9.0.0.tar.gz +5093c321bc47af2ea9afa726605ff1ce postgresql-9.0.1.tar.gz From 61e79cf6339480dc550770089e7a80a7689daeb2 Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Wed, 9 Feb 2011 07:15:37 -0600 Subject: [PATCH 21/24] - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild --- sepostgresql.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sepostgresql.spec b/sepostgresql.spec index b79d3e7..2e823f2 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -12,7 +12,7 @@ Summary: Security Enhanced PostgreSQL Name: sepostgresql Version: 9.0.1 -Release: 20101007%{?dist} +Release: 20101008%{?dist} License: PostgreSQL Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ @@ -181,6 +181,9 @@ fi %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog +* Wed Feb 09 2011 Fedora Release Engineering - 9.0.1-20101008 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + * Thu Oct 7 2010 KaiGai Kohei - 9.0.1-20101007 - upgrade base version to 9.0.1 From 81e0ec58393c18d2ab3cdf0f0587df57e752c5c5 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Fri, 15 Apr 2011 09:19:11 +0200 Subject: [PATCH 22/24] upgrade base version to 9.0.3, and initial labeling behavior was revised --- .gitignore | 1 + sepostgresql-9.0-fullset.patch | 911 ++++++++++++++++++--------------- sepostgresql.spec | 12 +- sources | 2 +- 4 files changed, 520 insertions(+), 406 deletions(-) diff --git a/.gitignore b/.gitignore index 1b4ef6d..81ff717 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ postgresql-9.0beta1.tar.gz /postgresql-9.0.0.tar.gz /postgresql-9.0.1.tar.gz +/postgresql-9.0.3.tar.gz diff --git a/sepostgresql-9.0-fullset.patch b/sepostgresql-9.0-fullset.patch index f3dddff..e4b3c26 100644 --- a/sepostgresql-9.0-fullset.patch +++ b/sepostgresql-9.0-fullset.patch @@ -1,5 +1,182 @@ + configure | 198 ++++++ + configure.in | 13 + + contrib/adminpack/adminpack.c | 28 +- + src/Makefile.global.in | 1 + + src/backend/Makefile | 7 +- + src/backend/access/common/heaptuple.c | 14 + + src/backend/access/common/tupdesc.c | 20 +- + src/backend/access/gin/ginutil.c | 2 +- + src/backend/access/heap/heapam.c | 35 + + src/backend/access/heap/tuptoaster.c | 6 + + src/backend/access/transam/twophase.c | 2 +- + src/backend/access/transam/xact.c | 19 + + src/backend/access/transam/xlog.c | 2 +- + src/backend/bootstrap/bootparse.y | 7 +- + src/backend/bootstrap/bootstrap.c | 14 + + src/backend/catalog/Makefile | 4 +- + src/backend/catalog/aclchk.c | 20 + + src/backend/catalog/catalog.c | 6 + + src/backend/catalog/genbki.pl | 3 +- + src/backend/catalog/heap.c | 120 +++- + src/backend/catalog/index.c | 28 +- + src/backend/catalog/namespace.c | 26 +- + src/backend/catalog/pg_aggregate.c | 9 +- + src/backend/catalog/pg_conversion.c | 4 +- + src/backend/catalog/pg_largeobject.c | 65 ++- + src/backend/catalog/pg_namespace.c | 4 +- + src/backend/catalog/pg_operator.c | 21 + + src/backend/catalog/pg_proc.c | 8 +- + src/backend/catalog/pg_seclabel.c | 646 +++++++++++++++++++ + src/backend/catalog/pg_type.c | 17 +- + src/backend/catalog/toasting.c | 12 +- + src/backend/commands/aggregatecmds.c | 4 + + src/backend/commands/alter.c | 61 ++ + src/backend/commands/cluster.c | 23 +- + src/backend/commands/comment.c | 60 ++ + src/backend/commands/conversioncmds.c | 16 +- + src/backend/commands/copy.c | 265 +++++++-- + src/backend/commands/dbcommands.c | 87 +++ + src/backend/commands/explain.c | 2 +- + src/backend/commands/foreigncmds.c | 39 ++ + src/backend/commands/functioncmds.c | 82 +++- + src/backend/commands/indexcmds.c | 18 +- + src/backend/commands/lockcmds.c | 4 + + src/backend/commands/opclasscmds.c | 48 ++- + src/backend/commands/operatorcmds.c | 6 + + src/backend/commands/prepare.c | 2 +- + src/backend/commands/proclang.c | 10 +- + src/backend/commands/schemacmds.c | 57 ++- + src/backend/commands/sequence.c | 16 + + src/backend/commands/tablecmds.c | 509 +++++++++++++++- + src/backend/commands/tablespace.c | 64 ++ + src/backend/commands/trigger.c | 11 + + src/backend/commands/tsearchcmds.c | 64 ++- + src/backend/commands/typecmds.c | 159 +++++- + src/backend/commands/user.c | 20 + + src/backend/commands/vacuum.c | 3 + + src/backend/commands/view.c | 4 + + src/backend/executor/execJunk.c | 5 +- + src/backend/executor/execMain.c | 63 ++- + src/backend/executor/execQual.c | 11 +- + src/backend/executor/execScan.c | 23 +- + src/backend/executor/execTuples.c | 17 +- + src/backend/executor/execUtils.c | 6 +- + src/backend/executor/functions.c | 6 +- + src/backend/executor/nodeAgg.c | 5 +- + src/backend/executor/nodeFunctionscan.c | 2 +- + src/backend/executor/nodeMergejoin.c | 4 + + src/backend/executor/nodeModifyTable.c | 76 +++- + src/backend/executor/nodeSubplan.c | 4 +- + src/backend/executor/nodeWindowAgg.c | 7 + + src/backend/executor/spi.c | 13 +- + src/backend/libpq/be-fsstubs.c | 40 +- + src/backend/nodes/copyfuncs.c | 19 + + src/backend/nodes/equalfuncs.c | 17 + + src/backend/nodes/outfuncs.c | 1 + + src/backend/nodes/readfuncs.c | 1 + + src/backend/optimizer/plan/planner.c | 11 + + src/backend/optimizer/util/clauses.c | 8 +- + src/backend/parser/analyze.c | 4 +- + src/backend/parser/gram.y | 120 ++++- + src/backend/parser/parse_relation.c | 12 +- + src/backend/parser/parse_target.c | 75 ++- + src/backend/parser/parse_utilcmd.c | 10 +- + src/backend/postmaster/postmaster.c | 45 ++- + src/backend/rewrite/rewriteDefine.c | 4 + + src/backend/rewrite/rewriteRemove.c | 4 + + src/backend/sepgsql/Makefile | 21 + + src/backend/sepgsql/attribute.c | 260 ++++++++ + src/backend/sepgsql/avc.c | 503 +++++++++++++++ + src/backend/sepgsql/blob.c | 245 ++++++++ + src/backend/sepgsql/conversion.c | 147 +++++ + src/backend/sepgsql/database.c | 201 ++++++ + src/backend/sepgsql/fdw.c | 296 +++++++++ + src/backend/sepgsql/file.c | 287 +++++++++ + src/backend/sepgsql/label.c | 588 ++++++++++++++++++ + src/backend/sepgsql/misc.c | 147 +++++ + src/backend/sepgsql/operator.c | 454 ++++++++++++++ + src/backend/sepgsql/proc.c | 366 +++++++++++ + src/backend/sepgsql/relation.c | 819 +++++++++++++++++++++++++ + src/backend/sepgsql/role.c | 142 +++++ + src/backend/sepgsql/rowlv.c | 346 +++++++++++ + src/backend/sepgsql/schema.c | 173 ++++++ + src/backend/sepgsql/selinux.c | 690 +++++++++++++++++++++ + src/backend/sepgsql/tablespace.c | 157 +++++ + src/backend/sepgsql/tsearch.c | 524 ++++++++++++++++ + src/backend/sepgsql/type.c | 314 ++++++++++ + src/backend/storage/large_object/inv_api.c | 4 +- + src/backend/tcop/fastpath.c | 5 + + src/backend/tcop/pquery.c | 2 +- + src/backend/tcop/utility.c | 49 ++ + src/backend/tsearch/wparser.c | 4 +- + src/backend/utils/adt/acl.c | 2 +- + src/backend/utils/adt/datetime.c | 4 +- + src/backend/utils/adt/dbsize.c | 6 + + src/backend/utils/adt/genfile.c | 9 +- + src/backend/utils/adt/lockfuncs.c | 2 +- + src/backend/utils/adt/misc.c | 2 +- + src/backend/utils/adt/pgstatfuncs.c | 2 +- + src/backend/utils/adt/ri_triggers.c | 24 + + src/backend/utils/adt/tid.c | 5 + + src/backend/utils/adt/trigfuncs.c | 4 + + src/backend/utils/adt/tsvector_op.c | 2 +- + src/backend/utils/cache/plancache.c | 8 +- + src/backend/utils/cache/relcache.c | 38 +- + src/backend/utils/cache/syscache.c | 24 + + src/backend/utils/fmgr/fmgr.c | 32 +- + src/backend/utils/fmgr/funcapi.c | 4 +- + src/backend/utils/init/postinit.c | 7 + + src/backend/utils/misc/guc.c | 74 ++- + src/backend/utils/misc/postgresql.conf.sample | 2 + + src/backend/utils/mmgr/portalmem.c | 2 +- + src/bin/initdb/initdb.c | 13 + + src/bin/pg_dump/pg_backup.h | 3 +- + src/bin/pg_dump/pg_backup_archiver.c | 81 +++- + src/bin/pg_dump/pg_backup_archiver.h | 9 +- + src/bin/pg_dump/pg_dump.c | 400 ++++++++++-- + src/bin/pg_dump/pg_dump.h | 7 + + src/bin/pg_dump/pg_dumpall.c | 60 ++- + src/bin/pg_dump/pg_restore.c | 6 + + src/include/access/htup.h | 35 +- + src/include/access/sysattr.h | 3 +- + src/include/access/tupdesc.h | 5 +- + src/include/bootstrap/bootstrap.h | 1 + + src/include/catalog/heap.h | 16 +- + src/include/catalog/indexing.h | 5 + + src/include/catalog/pg_class.h | 28 +- + src/include/catalog/pg_conversion_fn.h | 2 +- + src/include/catalog/pg_largeobject.h | 3 +- + src/include/catalog/pg_namespace.h | 2 +- + src/include/catalog/pg_proc.h | 4 + + src/include/catalog/pg_proc_fn.h | 3 +- + src/include/catalog/pg_seclabel.h | 79 +++ + src/include/catalog/pg_type_fn.h | 3 +- + src/include/catalog/toasting.h | 3 + + src/include/commands/alter.h | 1 + + src/include/commands/dbcommands.h | 1 + + src/include/commands/defrem.h | 2 + + src/include/commands/schemacmds.h | 1 + + src/include/commands/tablecmds.h | 7 + + src/include/commands/tablespace.h | 1 + + src/include/commands/typecmds.h | 2 + + src/include/executor/executor.h | 9 +- + src/include/fmgr.h | 1 + + src/include/nodes/nodes.h | 1 + + src/include/nodes/parsenodes.h | 17 + + src/include/parser/kwlist.h | 1 + + src/include/pg_config.h.in | 3 + + src/include/sepgsql/hooks.h | 293 +++++++++ + src/include/sepgsql/sepgsql.h | 318 ++++++++++ + src/include/storage/large_object.h | 2 +- + src/include/storage/lwlock.h | 1 + + src/include/utils/guc.h | 1 + + src/include/utils/syscache.h | 11 + + src/pl/plpgsql/src/pl_comp.c | 2 +- + src/test/regress/expected/sanity_check.out | 3 +- + 175 files changed, 11679 insertions(+), 375 deletions(-) + diff --git a/configure b/configure -index c4ba09e..bbed799 100755 +index a4f4c9e..e8e71c1 100755 --- a/configure +++ b/configure @@ -708,6 +708,7 @@ LDFLAGS_EX @@ -229,7 +406,7 @@ index c4ba09e..bbed799 100755 # diff --git a/configure.in b/configure.in -index c381505..14d0aba 100644 +index 26166d0..0a58c40 100644 --- a/configure.in +++ b/configure.in @@ -755,6 +755,19 @@ PGAC_ARG_BOOL(with, zlib, yes, @@ -554,7 +731,7 @@ index 2a75820..065ba1a 100644 TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL, INT2OID, -1, 0); diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c -index 48a387e..f00a9cc 100644 +index 1c93400..1236873 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -52,6 +52,7 @@ @@ -737,10 +914,10 @@ index 8436dea..d9de0e4 100644 CurrentTransactionState = s; diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c -index 15b3476..54a9105 100644 +index bb06a2e..77feebf 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c -@@ -8895,7 +8895,7 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS) +@@ -8916,7 +8916,7 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS) * Construct a tuple descriptor for the result row. This must match this * function's pg_proc entry! */ @@ -860,7 +1037,7 @@ index a970039..0381d6b 100644 pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ pg_default_acl.h \ diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c -index 0470a70..62fedb1 100644 +index 9b5602e..80be522 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -38,6 +38,7 @@ @@ -879,7 +1056,7 @@ index 0470a70..62fedb1 100644 #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" -@@ -1500,6 +1502,10 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, +@@ -1506,6 +1508,10 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) continue; @@ -890,7 +1067,7 @@ index 0470a70..62fedb1 100644 /* Views don't have any system columns at all */ if (classForm->relkind == RELKIND_VIEW && curr_att < 0) continue; -@@ -1609,6 +1615,8 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, +@@ -1615,6 +1621,8 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, relOid, grantorId, ACL_KIND_COLUMN, relname, attnum, NameStr(pg_attribute_tuple->attname)); @@ -899,7 +1076,7 @@ index 0470a70..62fedb1 100644 /* * Generate new ACL. -@@ -1870,6 +1878,8 @@ ExecGrant_Relation(InternalGrant *istmt) +@@ -1876,6 +1884,8 @@ ExecGrant_Relation(InternalGrant *istmt) ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, NameStr(pg_class_tuple->relname), 0, NULL); @@ -908,7 +1085,7 @@ index 0470a70..62fedb1 100644 /* * Generate new ACL. -@@ -2064,6 +2074,8 @@ ExecGrant_Database(InternalGrant *istmt) +@@ -2070,6 +2080,8 @@ ExecGrant_Database(InternalGrant *istmt) datId, grantorId, ACL_KIND_DATABASE, NameStr(pg_database_tuple->datname), 0, NULL); @@ -917,7 +1094,7 @@ index 0470a70..62fedb1 100644 /* * Generate new ACL. -@@ -2189,6 +2201,8 @@ ExecGrant_Fdw(InternalGrant *istmt) +@@ -2195,6 +2207,8 @@ ExecGrant_Fdw(InternalGrant *istmt) fdwid, grantorId, ACL_KIND_FDW, NameStr(pg_fdw_tuple->fdwname), 0, NULL); @@ -926,7 +1103,7 @@ index 0470a70..62fedb1 100644 /* * Generate new ACL. -@@ -2314,6 +2328,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) +@@ -2320,6 +2334,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) srvid, grantorId, ACL_KIND_FOREIGN_SERVER, NameStr(pg_server_tuple->srvname), 0, NULL); @@ -935,7 +1112,7 @@ index 0470a70..62fedb1 100644 /* * Generate new ACL. -@@ -2832,6 +2848,8 @@ ExecGrant_Namespace(InternalGrant *istmt) +@@ -2838,6 +2854,8 @@ ExecGrant_Namespace(InternalGrant *istmt) nspid, grantorId, ACL_KIND_NAMESPACE, NameStr(pg_namespace_tuple->nspname), 0, NULL); @@ -944,7 +1121,7 @@ index 0470a70..62fedb1 100644 /* * Generate new ACL. -@@ -2956,6 +2974,8 @@ ExecGrant_Tablespace(InternalGrant *istmt) +@@ -2962,6 +2980,8 @@ ExecGrant_Tablespace(InternalGrant *istmt) tblId, grantorId, ACL_KIND_TABLESPACE, NameStr(pg_tablespace_tuple->spcname), 0, NULL); @@ -1350,7 +1527,7 @@ index 47e0c9b..73cbfcf 100644 diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c -index dea6889..18d96da 100644 +index 8af382b..01f53fd 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -39,6 +39,7 @@ @@ -1361,7 +1538,7 @@ index dea6889..18d96da 100644 #include "catalog/pg_tablespace.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" -@@ -90,7 +91,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, +@@ -89,7 +90,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, Oid *classObjectId); static void InitializeAttributeOids(Relation indexRelation, int numatts, Oid indexoid); @@ -1371,7 +1548,7 @@ index dea6889..18d96da 100644 static void UpdateIndexRelation(Oid indexoid, Oid heapoid, IndexInfo *indexInfo, Oid *classOids, -@@ -155,7 +157,7 @@ ConstructTupleDescriptor(Relation heapRelation, +@@ -154,7 +156,7 @@ ConstructTupleDescriptor(Relation heapRelation, /* * allocate the new tuple descriptor */ @@ -1380,7 +1557,7 @@ index dea6889..18d96da 100644 /* * For simple index columns, we copy the pg_attribute row from the parent -@@ -182,7 +184,8 @@ ConstructTupleDescriptor(Relation heapRelation, +@@ -181,7 +183,8 @@ ConstructTupleDescriptor(Relation heapRelation, * here we are indexing on a system attribute (-1...-n) */ from = SystemAttributeDefinition(atnum, @@ -1390,7 +1567,7 @@ index dea6889..18d96da 100644 } else { -@@ -339,13 +342,16 @@ InitializeAttributeOids(Relation indexRelation, +@@ -338,13 +341,16 @@ InitializeAttributeOids(Relation indexRelation, * ---------------------------------------------------------------- */ static void @@ -1408,7 +1585,7 @@ index dea6889..18d96da 100644 /* * open the attribute relation and its indexes */ -@@ -367,7 +373,8 @@ AppendAttributeTuples(Relation indexRelation, int numatts) +@@ -366,7 +372,8 @@ AppendAttributeTuples(Relation indexRelation, int numatts) Assert(indexTupDesc->attrs[i]->attnum == i + 1); Assert(indexTupDesc->attrs[i]->attcacheoff == -1); @@ -1418,7 +1595,7 @@ index dea6889..18d96da 100644 } CatalogCloseIndexes(indstate); -@@ -545,6 +552,7 @@ index_create(Oid heapRelationId, +@@ -544,6 +551,7 @@ index_create(Oid heapRelationId, bool mapped_relation; bool is_exclusion; Oid namespaceId; @@ -1426,7 +1603,7 @@ index dea6889..18d96da 100644 int i; is_exclusion = (indexInfo->ii_ExclusionOps != NULL); -@@ -682,15 +690,21 @@ index_create(Oid heapRelationId, +@@ -681,15 +689,21 @@ index_create(Oid heapRelationId, indexRelation->rd_rel->relam = accessMethodObjectId; indexRelation->rd_rel->relkind = RELKIND_INDEX; indexRelation->rd_rel->relhasoids = false; @@ -1449,7 +1626,7 @@ index dea6889..18d96da 100644 /* done with pg_class */ heap_close(pg_class, RowExclusiveLock); -@@ -706,7 +720,7 @@ index_create(Oid heapRelationId, +@@ -705,7 +719,7 @@ index_create(Oid heapRelationId, /* * append ATTRIBUTE tuples for the index */ @@ -2716,7 +2893,7 @@ index cecdfa5..5b51f68 100644 + } +} diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c -index 7a1b8e8..69988d0 100644 +index 61020dc..f8408b4 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -30,12 +30,14 @@ @@ -2794,7 +2971,7 @@ index 7a1b8e8..69988d0 100644 /* The heap rewrite module does the rest */ rewrite_heap_tuple(rwstate, tuple, copiedTuple); -@@ -1464,7 +1480,8 @@ get_tables_to_cluster(MemoryContext cluster_context) +@@ -1469,7 +1485,8 @@ get_tables_to_cluster(MemoryContext cluster_context) { index = (Form_pg_index) GETSTRUCT(indexTuple); @@ -4140,7 +4317,7 @@ index 26a3a52..e2b8683 100644 ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c -index 780dbc2..f471123 100644 +index a129511..6c2dbc4 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -40,6 +40,7 @@ @@ -4192,7 +4369,7 @@ index 780dbc2..f471123 100644 + ReleaseSysCache(tuple); - if (!reindex_relation(heapOid, true, false)) + if (!reindex_relation(heapOid, true, 0)) @@ -1670,6 +1683,9 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, databaseName); @@ -4660,7 +4837,7 @@ index 292a427..3c8a36f 100644 if (!seqrel->rd_islocaltemp) PreventCommandIfReadOnly("setval()"); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c -index c4d81bb..9516700 100644 +index 6a1804b..2453fcf 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -32,6 +32,7 @@ @@ -4916,7 +5093,7 @@ index c4d81bb..9516700 100644 /* * Nothing to do here; Phase 3 does the work */ -@@ -3145,6 +3210,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) +@@ -3146,6 +3211,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) MemoryContext oldCxt; List *dropped_attrs = NIL; ListCell *lc; @@ -4924,7 +5101,7 @@ index c4d81bb..9516700 100644 econtext = GetPerTupleExprContext(estate); -@@ -3175,6 +3241,19 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) +@@ -3176,6 +3242,19 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) } /* @@ -4944,7 +5121,7 @@ index c4d81bb..9516700 100644 * Scan through the rows, generating a new row if needed and then * checking all the constraints. */ -@@ -3191,11 +3270,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) +@@ -3192,11 +3271,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) if (newrel) { Oid tupOid = InvalidOid; @@ -4961,7 +5138,7 @@ index c4d81bb..9516700 100644 /* Set dropped attributes to null in new tuple */ foreach(lc, dropped_attrs) -@@ -3227,6 +3311,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) +@@ -3228,6 +3312,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) /* Preserve OID, if any */ if (newTupDesc->tdhasoid) HeapTupleSetOid(tuple, tupOid); @@ -4971,7 +5148,7 @@ index c4d81bb..9516700 100644 } /* Now check any constraints on the possibly-changed tuple */ -@@ -3625,7 +3712,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, +@@ -3626,7 +3713,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, @@ -4980,7 +5157,7 @@ index c4d81bb..9516700 100644 { Oid myrelid = RelationGetRelid(rel); Relation pgclass, -@@ -3639,6 +3726,10 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3640,6 +3727,10 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, int32 typmod; Form_pg_type tform; Expr *defval; @@ -4991,7 +5168,7 @@ index c4d81bb..9516700 100644 attrdesc = heap_open(AttributeRelationId, RowExclusiveLock); -@@ -3667,6 +3758,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3668,6 +3759,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, errmsg("child table \"%s\" has different type for column \"%s\"", RelationGetRelationName(rel), colDef->colname))); @@ -5005,7 +5182,7 @@ index c4d81bb..9516700 100644 /* If it's OID, child column must actually be OID */ if (isOid && childatt->attnum != ObjectIdAttributeNumber) ereport(ERROR, -@@ -3674,6 +3772,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3675,6 +3773,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, errmsg("child table \"%s\" has a conflicting \"%s\" column", RelationGetRelationName(rel), colDef->colname))); @@ -5019,7 +5196,7 @@ index c4d81bb..9516700 100644 /* Bump the existing child att's inhcount */ childatt->attinhcount++; simple_heap_update(attrdesc, &tuple->t_self, tuple); -@@ -3713,6 +3818,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3714,6 +3819,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, /* Determine the new attribute's number */ if (isOid) newattnum = ObjectIdAttributeNumber; @@ -5028,7 +5205,7 @@ index c4d81bb..9516700 100644 else { newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1; -@@ -3752,7 +3859,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3753,7 +3860,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ReleaseSysCache(typeTuple); @@ -5037,7 +5214,7 @@ index c4d81bb..9516700 100644 heap_close(attrdesc, RowExclusiveLock); -@@ -3761,6 +3868,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3762,6 +3869,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, */ if (isOid) ((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true; @@ -5046,7 +5223,7 @@ index c4d81bb..9516700 100644 else ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum; -@@ -3872,7 +3981,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, +@@ -3873,7 +3982,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, * If we are adding an OID column, we have to tell Phase 3 to rewrite the * table to fix that. */ @@ -5055,7 +5232,7 @@ index c4d81bb..9516700 100644 tab->new_changeoids = true; /* -@@ -3925,6 +4034,31 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) +@@ -3926,6 +4035,31 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) } /* @@ -5087,7 +5264,7 @@ index c4d81bb..9516700 100644 * ALTER TABLE ALTER COLUMN DROP NOT NULL */ static void -@@ -3936,6 +4070,9 @@ ATExecDropNotNull(Relation rel, const char *colName) +@@ -3937,6 +4071,9 @@ ATExecDropNotNull(Relation rel, const char *colName) List *indexoidlist; ListCell *indexoidscan; @@ -5097,7 +5274,7 @@ index c4d81bb..9516700 100644 /* * lookup the attribute */ -@@ -4026,6 +4163,9 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, +@@ -4027,6 +4164,9 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, AttrNumber attnum; Relation attr_rel; @@ -5107,7 +5284,7 @@ index c4d81bb..9516700 100644 /* * lookup the attribute */ -@@ -4076,6 +4216,9 @@ ATExecColumnDefault(Relation rel, const char *colName, +@@ -4077,6 +4217,9 @@ ATExecColumnDefault(Relation rel, const char *colName, { AttrNumber attnum; @@ -5117,7 +5294,7 @@ index c4d81bb..9516700 100644 /* * get the number of the attribute */ -@@ -4150,6 +4293,9 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue) +@@ -4151,6 +4294,9 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue) HeapTuple tuple; Form_pg_attribute attrtuple; @@ -5127,7 +5304,7 @@ index c4d81bb..9516700 100644 Assert(IsA(newValue, Integer)); newtarget = intVal(newValue); -@@ -4216,6 +4362,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, +@@ -4217,6 +4363,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, bool repl_null[Natts_pg_attribute]; bool repl_repl[Natts_pg_attribute]; @@ -5137,7 +5314,7 @@ index c4d81bb..9516700 100644 attrelation = heap_open(AttributeRelationId, RowExclusiveLock); tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName); -@@ -4275,6 +4424,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue) +@@ -4276,6 +4425,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue) HeapTuple tuple; Form_pg_attribute attrtuple; @@ -5147,7 +5324,7 @@ index c4d81bb..9516700 100644 Assert(IsA(newValue, String)); storagemode = strVal(newValue); -@@ -4373,6 +4525,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, +@@ -4374,6 +4526,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, if (recursing) ATSimplePermissions(rel, false); @@ -5157,7 +5334,7 @@ index c4d81bb..9516700 100644 /* * get the number of the attribute */ -@@ -4398,8 +4553,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, +@@ -4399,8 +4554,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, attnum = targetatt->attnum; @@ -5170,7 +5347,7 @@ index c4d81bb..9516700 100644 ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot drop system column \"%s\"", -@@ -4515,7 +4672,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, +@@ -4516,7 +4673,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, * If we dropped the OID column, must adjust pg_class.relhasoids and tell * Phase 3 to physically get rid of the column. */ @@ -5180,7 +5357,7 @@ index c4d81bb..9516700 100644 { Relation class_rel; Form_pg_class tuple_class; -@@ -4530,7 +4688,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, +@@ -4531,7 +4689,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, RelationGetRelid(rel)); tuple_class = (Form_pg_class) GETSTRUCT(tuple); @@ -5193,7 +5370,7 @@ index c4d81bb..9516700 100644 simple_heap_update(class_rel, &tuple->t_self, tuple); /* Keep the catalog indexes up to date */ -@@ -4677,6 +4839,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, +@@ -4678,6 +4840,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, if (recursing) ATSimplePermissions(rel, false); @@ -5203,7 +5380,7 @@ index c4d81bb..9516700 100644 /* * Call AddRelationNewConstraints to do the work, making sure it works on * a copy of the Constraint so transformExpr can't modify the original. It -@@ -4883,6 +5048,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, +@@ -4884,6 +5049,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, checkFkeyPermissions(pkrel, pkattnum, numpks); checkFkeyPermissions(rel, fkattnum, numfks); @@ -5213,7 +5390,7 @@ index c4d81bb..9516700 100644 /* * Look up the equality operators to use in the constraint. * -@@ -5628,6 +5796,9 @@ ATExecDropConstraint(Relation rel, const char *constrName, +@@ -5629,6 +5797,9 @@ ATExecDropConstraint(Relation rel, const char *constrName, if (recursing) ATSimplePermissions(rel, false); @@ -5223,7 +5400,7 @@ index c4d81bb..9516700 100644 conrel = heap_open(ConstraintRelationId, RowExclusiveLock); /* -@@ -5965,6 +6136,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, +@@ -5966,6 +6137,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, SysScanDesc scan; HeapTuple depTup; @@ -5233,7 +5410,7 @@ index c4d81bb..9516700 100644 attrelation = heap_open(AttributeRelationId, RowExclusiveLock); /* Look up the target column */ -@@ -6578,6 +6752,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) +@@ -6596,6 +6770,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } @@ -5242,7 +5419,7 @@ index c4d81bb..9516700 100644 } memset(repl_null, false, sizeof(repl_null)); -@@ -6743,6 +6919,9 @@ ATExecClusterOn(Relation rel, const char *indexName) +@@ -6761,6 +6937,9 @@ ATExecClusterOn(Relation rel, const char *indexName) { Oid indexOid; @@ -5252,7 +5429,7 @@ index c4d81bb..9516700 100644 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace); if (!OidIsValid(indexOid)) -@@ -6767,6 +6946,9 @@ ATExecClusterOn(Relation rel, const char *indexName) +@@ -6785,6 +6964,9 @@ ATExecClusterOn(Relation rel, const char *indexName) static void ATExecDropCluster(Relation rel) { @@ -5262,7 +5439,7 @@ index c4d81bb..9516700 100644 mark_index_clustered(rel, InvalidOid); } -@@ -6817,6 +6999,9 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) +@@ -6835,6 +7017,9 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) bool repl_repl[Natts_pg_class]; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; @@ -5272,7 +5449,7 @@ index c4d81bb..9516700 100644 if (defList == NIL) return; /* nothing to do */ -@@ -7145,6 +7330,9 @@ static void +@@ -7163,6 +7348,9 @@ static void ATExecEnableDisableTrigger(Relation rel, char *trigname, char fires_when, bool skip_system) { @@ -5282,7 +5459,7 @@ index c4d81bb..9516700 100644 EnableDisableTrigger(rel, trigname, fires_when, skip_system); } -@@ -7157,6 +7345,9 @@ static void +@@ -7175,6 +7363,9 @@ static void ATExecEnableDisableRule(Relation rel, char *trigname, char fires_when) { @@ -5292,7 +5469,7 @@ index c4d81bb..9516700 100644 EnableDisableRule(rel, trigname, fires_when); } -@@ -7199,6 +7390,10 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent) +@@ -7217,6 +7408,10 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent) */ ATSimplePermissions(parent_rel, false); @@ -5303,7 +5480,7 @@ index c4d81bb..9516700 100644 /* Permanent rels cannot inherit from temporary ones */ if (parent_rel->rd_istemp && !child_rel->rd_istemp) ereport(ERROR, -@@ -7551,6 +7746,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent) +@@ -7569,6 +7764,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent) List *connames; bool found = false; @@ -5313,7 +5490,7 @@ index c4d81bb..9516700 100644 /* * AccessShareLock on the parent is probably enough, seeing that DROP * TABLE doesn't lock parent tables at all. We need some lock since we'll -@@ -7848,6 +8046,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, +@@ -7866,6 +8064,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, /* get schema OID and check its permissions */ nspOid = LookupCreationNamespace(newschema); @@ -5323,7 +5500,7 @@ index c4d81bb..9516700 100644 if (oldNspOid == nspOid) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE), -@@ -8045,6 +8246,282 @@ AlterSeqNamespaces(Relation classRel, Relation rel, +@@ -8063,6 +8264,282 @@ AlterSeqNamespaces(Relation classRel, Relation rel, relation_close(depRel, AccessShareLock); } @@ -6454,7 +6631,7 @@ index c291529..dd0b12a 100644 CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW"); diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c -index 2fa7e68..c2b926c 100644 +index da22709..cbd4932 100644 --- a/src/backend/executor/execJunk.c +++ b/src/backend/executor/execJunk.c @@ -58,7 +58,8 @@ @@ -6477,7 +6654,7 @@ index 2fa7e68..c2b926c 100644 /* * 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 0b6cbcc..fbce8e4 100644 +index 6d4a773..a8ad9a3 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -38,6 +38,7 @@ @@ -6520,7 +6697,7 @@ index 0b6cbcc..fbce8e4 100644 } } -@@ -829,6 +841,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) +@@ -826,6 +838,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) j = ExecInitJunkFilter(planstate->plan->targetlist, tupType->tdhasoid, @@ -6528,7 +6705,7 @@ index 0b6cbcc..fbce8e4 100644 ExecInitExtraTupleSlot(estate)); estate->es_junkFilter = j; -@@ -1065,6 +1078,37 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids) +@@ -1062,6 +1075,37 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids) return false; } @@ -6566,7 +6743,7 @@ index 0b6cbcc..fbce8e4 100644 /* ---------------------------------------------------------------- * ExecEndPlan * -@@ -2073,6 +2117,7 @@ OpenIntoRel(QueryDesc *queryDesc) +@@ -2130,6 +2174,7 @@ OpenIntoRel(QueryDesc *queryDesc) Oid intoRelationId; TupleDesc tupdesc; DR_intorel *myState; @@ -6574,7 +6751,7 @@ index 0b6cbcc..fbce8e4 100644 static char *validnsps[] = HEAP_RELOPT_NAMESPACES; Assert(into); -@@ -2144,6 +2189,14 @@ OpenIntoRel(QueryDesc *queryDesc) +@@ -2201,6 +2246,14 @@ OpenIntoRel(QueryDesc *queryDesc) get_tablespace_name(tablespaceId)); } @@ -6589,7 +6766,7 @@ index 0b6cbcc..fbce8e4 100644 /* Parse and validate any reloptions */ reloptions = transformRelOptions((Datum) 0, into->options, -@@ -2174,7 +2227,8 @@ OpenIntoRel(QueryDesc *queryDesc) +@@ -2231,7 +2284,8 @@ OpenIntoRel(QueryDesc *queryDesc) into->onCommit, reloptions, true, @@ -6599,7 +6776,7 @@ index 0b6cbcc..fbce8e4 100644 FreeTupleDesc(tupdesc); -@@ -2298,6 +2352,11 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self) +@@ -2355,6 +2409,11 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self) */ if (myState->rel->rd_rel->relhasoids) HeapTupleSetOid(tuple, InvalidOid); @@ -6823,7 +7000,7 @@ index 5886c1b..50f91f5 100644 } diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c -index ea2cfc9..7b489db 100644 +index 75fc173..99cb650 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -1153,7 +1153,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, @@ -6835,7 +7012,7 @@ index ea2cfc9..7b489db 100644 } else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) { -@@ -1192,7 +1192,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, +@@ -1197,7 +1197,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, } /* Set up junk filter if needed */ if (junkFilter) @@ -6844,7 +7021,7 @@ index ea2cfc9..7b489db 100644 return false; /* NOT returning whole tuple */ } } -@@ -1205,7 +1205,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, +@@ -1210,7 +1210,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, * what the caller expects will happen at runtime. */ if (junkFilter) @@ -6919,7 +7096,7 @@ index 948f580..7d9f4f4 100644 fmgr_info(cmpproc, &(clause->cmpfinfo)); diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c -index 9699381..03afb4e 100644 +index f4b2b16..62c76b5 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -38,11 +38,13 @@ @@ -7049,7 +7226,7 @@ index 9699381..03afb4e 100644 /* * Reset the per-output-tuple exprcontext. This is needed because * triggers expect to use that context as workspace. It's a bit ugly -@@ -713,6 +768,14 @@ ExecModifyTable(ModifyTableState *node) +@@ -714,6 +769,14 @@ ExecModifyTable(ModifyTableState *node) if (junkfilter != NULL) { @@ -7064,7 +7241,7 @@ index 9699381..03afb4e 100644 /* * extract the 'ctid' junk attribute. */ -@@ -742,11 +805,11 @@ ExecModifyTable(ModifyTableState *node) +@@ -743,11 +806,11 @@ ExecModifyTable(ModifyTableState *node) switch (operation) { case CMD_INSERT: @@ -7078,7 +7255,7 @@ index 9699381..03afb4e 100644 break; case CMD_DELETE: slot = ExecDelete(tupleid, planSlot, -@@ -858,7 +921,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) +@@ -855,7 +918,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * RETURNING list. We assume the rest will look the same. */ tupDesc = ExecTypeFromTL((List *) linitial(node->returningLists), @@ -7087,7 +7264,7 @@ index 9699381..03afb4e 100644 /* Set up a slot for the output of the RETURNING projection(s) */ ExecInitResultTupleSlot(estate, &mtstate->ps); -@@ -892,7 +955,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) +@@ -889,7 +952,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * We still must construct a dummy result tuple type, because InitPlan * expects one (maybe should change that?). */ @@ -7096,7 +7273,7 @@ index 9699381..03afb4e 100644 ExecInitResultTupleSlot(estate, &mtstate->ps); ExecAssignResultType(&mtstate->ps, tupDesc); -@@ -984,6 +1047,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) +@@ -986,6 +1049,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) j = ExecInitJunkFilter(subplan->targetlist, resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, @@ -7374,10 +7551,10 @@ index 464183d..67d0316 100644 PG_RETURN_INT32(0); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c -index 6e6844b..8d58a17 100644 +index 0b2aa3d..90bb7d4 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c -@@ -1825,6 +1825,7 @@ _copyRangeTblEntry(RangeTblEntry *from) +@@ -1823,6 +1823,7 @@ _copyRangeTblEntry(RangeTblEntry *from) COPY_SCALAR_FIELD(checkAsUser); COPY_BITMAPSET_FIELD(selectedCols); COPY_BITMAPSET_FIELD(modifiedCols); @@ -7385,7 +7562,7 @@ index 6e6844b..8d58a17 100644 return newnode; } -@@ -2757,6 +2758,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) +@@ -2755,6 +2756,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) return newnode; } @@ -7407,7 +7584,7 @@ index 6e6844b..8d58a17 100644 static RuleStmt * _copyRuleStmt(RuleStmt *from) { -@@ -3971,6 +3987,9 @@ copyObject(void *from) +@@ -3972,6 +3988,9 @@ copyObject(void *from) case T_AlterOwnerStmt: retval = _copyAlterOwnerStmt(from); break; @@ -7460,10 +7637,10 @@ index 6e00584..2adfa91 100644 retval = _equalRuleStmt(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c -index ae0e2ab..36eee35 100644 +index 032150e..63f6b77 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c -@@ -2137,6 +2137,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) +@@ -2143,6 +2143,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) WRITE_OID_FIELD(checkAsUser); WRITE_BITMAPSET_FIELD(selectedCols); WRITE_BITMAPSET_FIELD(modifiedCols); @@ -7484,7 +7661,7 @@ index f28191d..f6f31e5 100644 READ_DONE(); } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c -index 317a7e8..5b88ad6 100644 +index 45ba902..be917ec 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -39,6 +39,7 @@ @@ -7513,7 +7690,7 @@ index 317a7e8..5b88ad6 100644 * pullup (so that all non-inherited RTEs are present) and before * inheritance expansion (so that the info is available for diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c -index 8486516..acd6bac 100644 +index 8902b5b..321bba9 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -38,6 +38,7 @@ @@ -7524,7 +7701,7 @@ index 8486516..acd6bac 100644 #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/builtins.h" -@@ -3724,6 +3725,10 @@ inline_function(Oid funcid, Oid result_type, List *args, +@@ -3752,6 +3753,10 @@ inline_function(Oid funcid, Oid result_type, List *args, if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) return NULL; @@ -7535,7 +7712,7 @@ index 8486516..acd6bac 100644 /* * Make a temporary memory context, so that we don't leak all the stuff * that parsing might create. -@@ -4176,7 +4181,8 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) +@@ -4205,7 +4210,8 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) funcform->provolatile == PROVOLATILE_VOLATILE || funcform->prosecdef || !funcform->proretset || @@ -7546,7 +7723,7 @@ index 8486516..acd6bac 100644 ReleaseSysCache(func_tuple); return NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c -index 1576613..51e197f 100644 +index 70cdb59..1a88724 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -25,6 +25,7 @@ @@ -7565,7 +7742,7 @@ index 1576613..51e197f 100644 #include "utils/rel.h" -@@ -651,7 +653,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) +@@ -660,7 +662,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) tle = makeTargetEntry(expr, attr_num, col->name, @@ -7575,7 +7752,7 @@ index 1576613..51e197f 100644 rte->modifiedCols = bms_add_member(rte->modifiedCols, diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y -index 50ed871..d6b13da 100644 +index 05ff082..acb1799 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -183,8 +183,8 @@ static TypeName *TableFuncTypeName(List *columns); @@ -7615,7 +7792,7 @@ index 50ed871..d6b13da 100644 | AlterSeqStmt | AlterTableStmt | AlterRoleSetStmt -@@ -1758,6 +1761,20 @@ alter_table_cmd: +@@ -1761,6 +1764,20 @@ alter_table_cmd: n->subtype = AT_DropOids; $$ = (Node *)n; } @@ -7636,7 +7813,7 @@ index 50ed871..d6b13da 100644 /* ALTER TABLE CLUSTER ON */ | CLUSTER ON name { -@@ -6027,6 +6044,102 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId +@@ -6028,6 +6045,102 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId } ; @@ -7980,7 +8157,7 @@ index d1e1dac..8e994da 100644 cxt.ckconstraints = NIL; cxt.fkconstraints = NIL; diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c -index 4666fd6..784723d 100644 +index 279f0f0..f91f746 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -109,6 +109,7 @@ @@ -8021,7 +8198,7 @@ index 4666fd6..784723d 100644 /* If we need to signal the autovacuum launcher, do so now */ if (avlauncher_needs_signal) { -@@ -2120,6 +2128,8 @@ SIGHUP_handler(SIGNAL_ARGS) +@@ -2125,6 +2133,8 @@ SIGHUP_handler(SIGNAL_ARGS) signal_child(SysLoggerPID, SIGHUP); if (PgStatPID != 0) signal_child(PgStatPID, SIGHUP); @@ -8030,7 +8207,7 @@ index 4666fd6..784723d 100644 /* Reload authentication config files too */ if (!load_hba()) -@@ -2180,6 +2190,9 @@ pmdie(SIGNAL_ARGS) +@@ -2185,6 +2195,9 @@ pmdie(SIGNAL_ARGS) /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); @@ -8040,7 +8217,7 @@ index 4666fd6..784723d 100644 /* * If we're in recovery, we can't kill the startup process -@@ -2242,6 +2255,9 @@ pmdie(SIGNAL_ARGS) +@@ -2247,6 +2260,9 @@ pmdie(SIGNAL_ARGS) /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); @@ -8050,7 +8227,7 @@ index 4666fd6..784723d 100644 pmState = PM_WAIT_BACKENDS; } -@@ -2277,6 +2293,8 @@ pmdie(SIGNAL_ARGS) +@@ -2282,6 +2298,8 @@ pmdie(SIGNAL_ARGS) signal_child(PgArchPID, SIGQUIT); if (PgStatPID != 0) signal_child(PgStatPID, SIGQUIT); @@ -8059,7 +8236,7 @@ index 4666fd6..784723d 100644 ExitPostmaster(0); break; } -@@ -2549,6 +2567,16 @@ reaper(SIGNAL_ARGS) +@@ -2554,6 +2572,16 @@ reaper(SIGNAL_ARGS) continue; } @@ -8076,7 +8253,7 @@ index 4666fd6..784723d 100644 /* * Else do standard backend child cleanup. */ -@@ -2765,6 +2793,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) +@@ -2770,6 +2798,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); } @@ -8095,7 +8272,7 @@ index 4666fd6..784723d 100644 /* * 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 -@@ -2921,7 +2961,8 @@ PostmasterStateMachine(void) +@@ -2926,7 +2966,8 @@ PostmasterStateMachine(void) WalReceiverPID == 0 && (BgWriterPID == 0 || !FatalError) && WalWriterPID == 0 && @@ -10159,10 +10336,10 @@ index 0000000..9a9e8a3 +} diff --git a/src/backend/sepgsql/label.c b/src/backend/sepgsql/label.c new file mode 100644 -index 0000000..7ab355c +index 0000000..ec8244d --- /dev/null +++ b/src/backend/sepgsql/label.c -@@ -0,0 +1,656 @@ +@@ -0,0 +1,588 @@ +/* + * label.c + * SE-PostgreSQL security label management @@ -10192,6 +10369,7 @@ index 0000000..7ab355c +#include "utils/tqual.h" +#include // for workaround hack +#include ++#include + +/* + * guc parameter to turn on/off mcstrans @@ -10380,256 +10558,212 @@ index 0000000..7ab355c + return nsid.secid; +} + -+/* -+ * a workaround implementation until libselinux/refpolicy don't -+ * support db_schema or other object classes. -+ */ -+static struct { -+ uint16 tclass; -+ char *pattern; -+ char *context; -+} initial_label_catalog[] = { -+ {SEPG_CLASS_DB_DATABASE, "*", -+ "system_u:object_r:sepgsql_db_t:s0"}, -+ {SEPG_CLASS_DB_SCHEMA, "*.*", -+ "system_u:object_r:sepgsql_db_t:s0"}, -+ {SEPG_CLASS_DB_TABLE, "*.pg_catalog.*", -+ "system_u:object_r:sepgsql_sysobj_t:s0"}, -+ {SEPG_CLASS_DB_TABLE, "*.*.*", -+ "system_u:object_r:sepgsql_table_t:s0"}, -+ {SEPG_CLASS_DB_VIEW, "*.*.*", -+ "system_u:object_r:sepgsql_db_t:s0"}, -+ {SEPG_CLASS_DB_SEQUENCE, "*.*.*", -+ "system_u:object_r:sepgsql_db_t:s0"}, -+ {SEPG_CLASS_DB_PROCEDURE, "*.pg_catalog.*", -+ "system_u:object_r:sepgsql_proc_exec_t:s0"}, -+ {SEPG_CLASS_DB_PROCEDURE, "*.*.*", -+ "system_u:object_r:sepgsql_user_proc_exec_t:s0"}, -+ {SEPG_CLASS_DB_COLUMN, "*.pg_catalog.*.*", -+ "system_u:object_r:sepgsql_sysobj_t:s0"}, -+ {SEPG_CLASS_DB_COLUMN, "*.*.*.*", -+ "system_u:object_r:sepgsql_table_t:s0"}, -+ {SEPG_CLASS_DB_TUPLE, "*.pg_catalog.*", -+ "system_u:object_r:sepgsql_sysobj_t:s0"}, -+ {SEPG_CLASS_DB_TUPLE, "*.*.*", -+ "system_u:object_r:sepgsql_table_t:s0"}, -+ {SEPG_CLASS_DB_BLOB, "*.*", -+ "system_u:object_r:sepgsql_blob_t:s0"}, -+ {0, NULL, NULL}, -+}; -+ -+static char * -+lookup_init_catalog(uint16 tclass, const char *name) ++static Oid ++sepgsql_initial_labeling_lookup(struct selabel_handle *sehnd, ++ Oid relationId, HeapTuple tuple) +{ -+ int i; -+ -+ for (i = 0; initial_label_catalog[i].pattern; i++) -+ { -+ if (initial_label_catalog[i].tclass == tclass && -+ fnmatch(initial_label_catalog[i].pattern, name, 0) == 0) -+ return initial_label_catalog[i].context; -+ } -+ elog(ERROR, "no valid initial security context for %s (tclass=%d)", -+ name, tclass); -+ return NULL; /* for compiler quiet */ -+} -+ -+static char * -+lookup_init_tuple_label(Oid relOid, HeapTuple tuple) -+{ -+ Oid relNsp = get_rel_namespace(relOid); -+ char namebuf[NAMEDATALEN * 3 + 10]; -+ -+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", -+ get_database_name(MyDatabaseId), -+ get_namespace_name(relNsp), -+ get_rel_name(relOid)); -+ -+ return lookup_init_catalog(SEPG_CLASS_DB_TUPLE, namebuf); -+} -+ -+static char * -+lookup_init_database_label(HeapTuple tuple) -+{ -+ Form_pg_database datForm = (Form_pg_database) GETSTRUCT(tuple); -+ -+ return lookup_init_catalog(SEPG_CLASS_DB_DATABASE, -+ NameStr(datForm->datname)); -+} -+ -+static char * -+lookup_init_schema_label(HeapTuple tuple) -+{ -+ Form_pg_namespace nspForm = (Form_pg_namespace) GETSTRUCT(tuple); -+ char namebuf[NAMEDATALEN * 2 + 10]; -+ -+ snprintf(namebuf, sizeof(namebuf), "%s.%s", -+ get_database_name(MyDatabaseId), -+ NameStr(nspForm->nspname)); -+ -+ return lookup_init_catalog(SEPG_CLASS_DB_SCHEMA, namebuf); -+} -+ -+static char * -+lookup_init_relation_label(HeapTuple tuple) -+{ -+ Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); -+ const char *relName = NameStr(classForm->relname); -+ Oid relNsp = classForm->relnamespace; -+ char *seclabel; ++ Form_pg_database datForm; ++ Form_pg_namespace nspForm; ++ Form_pg_class clsForm; ++ Form_pg_attribute attForm; ++ Form_pg_proc proForm; + char namebuf[NAMEDATALEN * 3 + 10]; ++ Oid namespaceId; ++ Oid securityId = InvalidOid; ++ int type = 0; ++ security_context_t context; + -+ switch (classForm->relkind) ++ switch (relationId) + { -+ case RELKIND_RELATION: ++ case DatabaseRelationId: ++ datForm = (Form_pg_database) GETSTRUCT(tuple); ++ snprintf(namebuf, sizeof(namebuf), "%s", ++ NameStr(datForm->datname)); ++ type = SELABEL_DB_DATABASE; ++ break; ++ ++ case NamespaceRelationId: ++ nspForm = (Form_pg_namespace) GETSTRUCT(tuple); ++ snprintf(namebuf, sizeof(namebuf), "%s.%s", ++ get_database_name(MyDatabaseId), ++ NameStr(nspForm->nspname)); ++ type = SELABEL_DB_SCHEMA; ++ break; ++ ++ case RelationRelationId: ++ clsForm = (Form_pg_class) GETSTRUCT(tuple); ++ namespaceId = clsForm->relnamespace; ++ switch (clsForm->relkind) ++ { ++ case RELKIND_RELATION: ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(namespaceId), ++ NameStr(clsForm->relname)); ++ type = SELABEL_DB_TABLE; ++ break; ++ ++ case RELKIND_SEQUENCE: ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(namespaceId), ++ NameStr(clsForm->relname)); ++ type = SELABEL_DB_SEQUENCE; ++ break; ++ ++ case RELKIND_VIEW: ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(namespaceId), ++ NameStr(clsForm->relname)); ++ type = SELABEL_DB_VIEW; ++ break; ++ ++ case RELKIND_INDEX: { ++ HeapTuple tbltup; ++ HeapTuple indtup; ++ Oid tableId; ++ ++ indtup = SearchSysCache1(INDEXRELID, ++ ObjectIdGetDatum(HeapTupleGetOid(tuple))); ++ if (!HeapTupleIsValid(indtup)) ++ elog(ERROR, "cache lookup failed for index %u", ++ HeapTupleGetOid(tuple)); ++ ++ tableId = ((Form_pg_index) GETSTRUCT(indtup))->indrelid; ++ tbltup = SearchSysCache1(RELOID, ObjectIdGetDatum(tableId)); ++ if (!HeapTupleIsValid(tbltup)) ++ elog(ERROR, "cache lookup failed for relation %u", tableId); ++ ++ securityId = sepgsql_initial_labeling_lookup(sehnd, RelationRelationId, tbltup); ++ ++ ReleaseSysCache(tbltup); ++ ReleaseSysCache(indtup); ++ ++ return securityId; ++ } ++ case RELKIND_TOASTVALUE: { ++ HeapTuple tbltup; ++ Oid tableId; ++ ++ /* ++ * XXX - we assume all the toast relation's name is ++ * "pg_toast_%u", and the "%u" shall be replaced by OID ++ * of the relation which owns the toast relation ++ */ ++ tableId = strtoul(NameStr(clsForm->relname) + 9, NULL, 10); ++ ++ tbltup = SearchSysCache1(RELOID, ++ ObjectIdGetDatum(tableId)); ++ if (!HeapTupleIsValid(tbltup)) ++ elog(ERROR, "cache lookup failed for relation %u", tableId); ++ ++ securityId = sepgsql_initial_labeling_lookup(sehnd, RelationRelationId, tbltup); ++ ++ ReleaseSysCache(tbltup); ++ ++ return securityId; ++ } ++ case RELKIND_COMPOSITE_TYPE: { ++ HeapTuple typtup; ++ ++ typtup = SearchSysCache1(TYPEOID, ++ ObjectIdGetDatum(clsForm->reltype)); ++ if (!HeapTupleIsValid(typtup)) ++ elog(ERROR, "cache lookup failed for type %u", clsForm->reltype); ++ ++ securityId = sepgsql_initial_labeling_lookup(sehnd, TypeRelationId, typtup); ++ ++ ReleaseSysCache(typtup); ++ ++ return securityId; ++ } ++ default: ++ elog(ERROR, "unexpected relkind %c of \"%s\"", ++ clsForm->relkind, NameStr(clsForm->relname)); ++ break; ++ } ++ break; ++ ++ case AttributeRelationId: ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ if (get_rel_relkind(attForm->attrelid) == RELKIND_RELATION) ++ { ++ namespaceId = get_rel_namespace(attForm->attrelid); ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(namespaceId), ++ get_rel_name(attForm->attrelid), ++ NameStr(attForm->attname)); ++ type = SELABEL_DB_COLUMN; ++ } ++ else ++ { ++ HeapTuple classTup; ++ ++ classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(attForm->attrelid)); ++ if (!HeapTupleIsValid(classTup)) ++ elog(ERROR, "cache lookup failed for relation %u", attForm->attrelid); ++ ++ securityId = sepgsql_initial_labeling_lookup(sehnd, RelationRelationId, classTup); ++ ++ ReleaseSysCache(classTup); ++ ++ return securityId; ++ } ++ break; ++ ++ case ProcedureRelationId: ++ proForm = (Form_pg_proc) GETSTRUCT(tuple); ++ namespaceId = proForm->pronamespace; + snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", + get_database_name(MyDatabaseId), -+ get_namespace_name(relNsp), relName); -+ seclabel = lookup_init_catalog(SEPG_CLASS_DB_TABLE, namebuf); ++ get_namespace_name(namespaceId), ++ NameStr(proForm->proname)); ++ type = SELABEL_DB_PROCEDURE; + break; + -+ case RELKIND_SEQUENCE: -+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ case LargeObjectMetadataRelationId: ++ snprintf(namebuf, sizeof(namebuf), "%s.%u", + get_database_name(MyDatabaseId), -+ get_namespace_name(relNsp), relName); -+ seclabel = lookup_init_catalog(SEPG_CLASS_DB_SEQUENCE, namebuf); ++ HeapTupleGetOid(tuple)); ++ type = SELABEL_DB_BLOB; + break; + -+ case RELKIND_VIEW: -+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", -+ get_database_name(MyDatabaseId), -+ get_namespace_name(relNsp), relName); -+ seclabel = lookup_init_catalog(SEPG_CLASS_DB_VIEW, namebuf); -+ break; -+ -+ case RELKIND_INDEX: { -+ HeapTuple tbltup; -+ HeapTuple indtup; -+ Oid tblOid; -+ Oid indOid = HeapTupleGetOid(tuple); -+ -+ indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indOid)); -+ if (!HeapTupleIsValid(indtup)) -+ elog(ERROR, "cache lookup failed for index %u", indOid); -+ -+ tblOid = ((Form_pg_index) GETSTRUCT(indtup))->indrelid; -+ tbltup = SearchSysCache1(RELOID, ObjectIdGetDatum(tblOid)); -+ if (!HeapTupleIsValid(tbltup)) -+ elog(ERROR, "cache lookup failed for relation %u", tblOid); -+ -+ seclabel = lookup_init_relation_label(tbltup); -+ -+ ReleaseSysCache(tbltup); -+ ReleaseSysCache(indtup); -+ -+ break; -+ } -+ case RELKIND_TOASTVALUE: { -+ HeapTuple tbltup; -+ Oid tblOid; -+ -+ /* -+ * XXX - we assume all the toast relation's name is -+ * "pg_toast_%u", and the "%u" shall be replaced by OID -+ * of the relation which owns the toast relation -+ */ -+ tblOid = strtoul(relName + 9, NULL, 10); -+ -+ tbltup = SearchSysCache1(RELOID, -+ ObjectIdGetDatum(tblOid)); -+ if (!HeapTupleIsValid(tbltup)) -+ elog(ERROR, "cache lookup failed for relation %u", tblOid); -+ -+ seclabel = lookup_init_relation_label(tbltup); -+ -+ ReleaseSysCache(tbltup); -+ -+ break; -+ } -+ case RELKIND_COMPOSITE_TYPE: { -+ Oid typOid = classForm->reltype; -+ HeapTuple typtup; -+ -+ typtup = SearchSysCache1(TYPEOID, -+ ObjectIdGetDatum(typOid)); -+ if (!HeapTupleIsValid(typtup)) -+ elog(ERROR, "cache lookup failed for type %u", typOid); -+ -+ seclabel = lookup_init_tuple_label(TypeRelationId, typtup); -+ -+ ReleaseSysCache(typtup); -+ -+ break; -+ } + default: -+ elog(ERROR, "unexpected relkind %c of \"%s\"", -+ classForm->relkind, relName); -+ seclabel = NULL; /* compiler quiet */ ++ namespaceId = get_rel_namespace(relationId); ++ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", ++ get_database_name(MyDatabaseId), ++ get_namespace_name(namespaceId), ++ get_rel_name(relationId)); ++ type = SELABEL_DB_TUPLE; + break; + } -+ return seclabel; -+} + -+static char * -+lookup_init_attribute_label(HeapTuple tuple) -+{ -+ Form_pg_attribute attForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ Oid tblOid = attForm->attrelid; -+ char *seclabel; -+ char namebuf[NAMEDATALEN * 4 + 10]; -+ -+ if (get_rel_relkind(tblOid) == RELKIND_RELATION) ++ if (selabel_lookup_raw(sehnd, &context, namebuf, type) == 0) + { -+ Oid tblNsp = get_rel_namespace(tblOid); -+ -+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s.%s", -+ get_database_name(MyDatabaseId), -+ get_namespace_name(tblNsp), -+ get_rel_name(tblOid), -+ NameStr(attForm->attname)); -+ -+ seclabel = lookup_init_catalog(SEPG_CLASS_DB_COLUMN, namebuf); ++ PG_TRY(); ++ { ++ securityId = seclabelTransInput(relationId, context); ++ } ++ PG_CATCH(); ++ { ++ freecon(context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(context); + } ++ else if (errno == ENOENT) ++ elog(WARNING, ++ "SELinux: no initial label assigned for %s (type=%d), skipping", ++ namebuf, type); + else -+ { -+ HeapTuple tbltup; ++ elog(ERROR, ++ "SELinux: could not determine initial security label for %s (type=%d): %m", ++ namebuf, type); + -+ tbltup = SearchSysCache1(RELOID, ObjectIdGetDatum(tblOid)); -+ if (!HeapTupleIsValid(tbltup)) -+ elog(ERROR, "cache lookup failed for relation %u", tblOid); -+ -+ seclabel = lookup_init_relation_label(tbltup); -+ -+ ReleaseSysCache(tbltup); -+ } -+ return seclabel; -+} -+ -+static char * -+lookup_init_procedure_label(HeapTuple tuple) -+{ -+ Form_pg_proc proForm = (Form_pg_proc) GETSTRUCT(tuple); -+ Oid proNsp = proForm->pronamespace; -+ char namebuf[NAMEDATALEN * 3 + 10]; -+ -+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", -+ get_database_name(MyDatabaseId), -+ get_namespace_name(proNsp), -+ NameStr(proForm->proname)); -+ -+ return lookup_init_catalog(SEPG_CLASS_DB_PROCEDURE, namebuf); -+} -+ -+static char * -+lookup_init_largeobject_label(HeapTuple tuple) -+{ -+ char namebuf[NAMEDATALEN + 20]; -+ -+ snprintf(namebuf, sizeof(namebuf), "%s.%u", -+ get_database_name(MyDatabaseId), -+ HeapTupleGetOid(tuple)); -+ -+ return lookup_init_catalog(SEPG_CLASS_DB_BLOB, namebuf); ++ return securityId; +} + +void @@ -10639,15 +10773,16 @@ index 0000000..7ab355c + SysScanDesc classScan; + ScanKeyData classSkey; + HeapTuple classTup; -+ Relation rel; -+ HeapScanDesc scan; -+ HeapTuple oldtup; -+ HeapTuple newtup; ++ struct selabel_handle *sehnd; + + Assert(IsBootstrapProcessingMode()); + + StartTransactionCommand(); + ++ sehnd = selabel_open(SELABEL_CTX_DB, NULL, 0); ++ if (!sehnd) ++ elog(ERROR, "failed to open initial selinux label"); ++ + classRel = heap_open(RelationRelationId, AccessShareLock); + + ScanKeyInit(&classSkey, @@ -10660,7 +10795,11 @@ index 0000000..7ab355c + + while (HeapTupleIsValid(classTup = systable_getnext(classScan))) + { -+ Oid relOid = HeapTupleGetOid(classTup); ++ Relation rel; ++ HeapScanDesc scan; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ Oid relOid = HeapTupleGetOid(classTup); + + Assert(((Form_pg_class) GETSTRUCT(classTup))->relhassecids); + @@ -10670,52 +10809,20 @@ index 0000000..7ab355c + + while (HeapTupleIsValid(oldtup = heap_getnext(scan, ForwardScanDirection))) + { -+ char *label; -+ Oid secid; ++ Oid securityId = sepgsql_initial_labeling_lookup(sehnd, relOid, oldtup); + -+ switch (relOid) -+ { -+ case DatabaseRelationId: -+ label = lookup_init_database_label(oldtup); -+ break; -+ -+ case NamespaceRelationId: -+ label = lookup_init_schema_label(oldtup); -+ break; -+ -+ case RelationRelationId: -+ label = lookup_init_relation_label(oldtup); -+ break; -+ -+ case AttributeRelationId: -+ label = lookup_init_attribute_label(oldtup); -+ break; -+ -+ case ProcedureRelationId: -+ label = lookup_init_procedure_label(oldtup); -+ break; -+ -+ case LargeObjectMetadataRelationId: -+ label = lookup_init_largeobject_label(oldtup); -+ break; -+ -+ default: -+ label = lookup_init_tuple_label(relOid, oldtup); -+ break; -+ } + /* + * inplace-updating + */ + newtup = heap_copytuple(oldtup); + -+ secid = seclabelTransInput(relOid, label); -+ -+ HeapTupleSetSecid(newtup, secid); ++ HeapTupleSetSecid(newtup, securityId); + + heap_inplace_update(rel, newtup); + + heap_freetuple(newtup); + } ++ + heap_endscan(scan); + + heap_close(rel, RowExclusiveLock); @@ -10724,6 +10831,8 @@ index 0000000..7ab355c + + heap_close(classRel, AccessShareLock); + ++ selabel_close(sehnd); ++ + CommitTransactionCommand(); +} + @@ -15983,7 +16092,7 @@ index 4e55b16..a9f9f7e 100644 TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample -index e1f5ab6..43f47f6 100644 +index a3880da..7537ac8 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -76,6 +76,8 @@ @@ -16082,10 +16191,10 @@ index 5a73779..67fce14 100644 const char *defn, const char *dropStmt, const char *copyStmt, diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c -index be5339e..8352c93 100644 +index 6528f4d..e482bde 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c -@@ -540,6 +540,8 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, +@@ -542,6 +542,8 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, } else { @@ -16094,7 +16203,7 @@ index be5339e..8352c93 100644 _disableTriggersIfNecessary(AH, te, ropt); /* Select owner and schema as necessary */ -@@ -575,6 +577,16 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, +@@ -577,6 +579,16 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, "ONLY " : ""), fmtId(te->tag)); } @@ -16111,7 +16220,7 @@ index be5339e..8352c93 100644 /* * If we have a copy statement, use it. As of V1.3, these -@@ -595,6 +607,9 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, +@@ -597,6 +609,9 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, AH->writingCopyData = false; @@ -16121,7 +16230,7 @@ index be5339e..8352c93 100644 /* close out the transaction started above */ if (is_parallel && te->created) CommitTransaction(AH); -@@ -712,7 +727,7 @@ ArchiveEntry(Archive *AHX, +@@ -714,7 +729,7 @@ ArchiveEntry(Archive *AHX, const char *tag, const char *namespace, const char *tablespace, @@ -16130,7 +16239,7 @@ index be5339e..8352c93 100644 const char *desc, teSection section, const char *defn, const char *dropStmt, const char *copyStmt, -@@ -744,6 +759,7 @@ ArchiveEntry(Archive *AHX, +@@ -746,6 +761,7 @@ ArchiveEntry(Archive *AHX, newToc->tablespace = tablespace ? strdup(tablespace) : NULL; newToc->owner = strdup(owner); newToc->withOids = withOids; @@ -16138,7 +16247,7 @@ index be5339e..8352c93 100644 newToc->desc = strdup(desc); newToc->defn = strdup(defn); newToc->dropStmt = strdup(dropStmt); -@@ -2046,6 +2062,7 @@ WriteToc(ArchiveHandle *AH) +@@ -2048,6 +2064,7 @@ WriteToc(ArchiveHandle *AH) WriteStr(AH, te->tablespace); WriteStr(AH, te->owner); WriteStr(AH, te->withOids ? "true" : "false"); @@ -16146,7 +16255,7 @@ index be5339e..8352c93 100644 /* Dump list of dependencies */ for (i = 0; i < te->nDeps; i++) -@@ -2157,6 +2174,16 @@ ReadToc(ArchiveHandle *AH) +@@ -2159,6 +2176,16 @@ ReadToc(ArchiveHandle *AH) else te->withOids = true; @@ -16163,7 +16272,7 @@ index be5339e..8352c93 100644 /* Read TOC entry dependencies */ if (AH->version >= K_VERS_1_5) { -@@ -2275,6 +2302,9 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) +@@ -2277,6 +2304,9 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te)) return 0; @@ -16173,7 +16282,7 @@ index be5339e..8352c93 100644 /* Ignore DATABASE entry unless we should create it */ if (!ropt->createDB && strcmp(te->desc, "DATABASE") == 0) return 0; -@@ -2341,6 +2371,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) +@@ -2343,6 +2373,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) (strcmp(te->desc, "ACL") == 0 && strncmp(te->tag, "LARGE OBJECT ", 13) == 0) || (strcmp(te->desc, "COMMENT") == 0 && @@ -16182,7 +16291,7 @@ index be5339e..8352c93 100644 strncmp(te->tag, "LARGE OBJECT ", 13) == 0)) res = res & REQ_DATA; else -@@ -2492,6 +2524,36 @@ _doSetWithOids(ArchiveHandle *AH, const bool withOids) +@@ -2494,6 +2526,36 @@ _doSetWithOids(ArchiveHandle *AH, const bool withOids) destroyPQExpBuffer(cmd); } @@ -16219,7 +16328,7 @@ index be5339e..8352c93 100644 /* * Issue the commands to connect to the specified database. -@@ -2590,6 +2652,18 @@ _setWithOids(ArchiveHandle *AH, TocEntry *te) +@@ -2592,6 +2654,18 @@ _setWithOids(ArchiveHandle *AH, TocEntry *te) } } @@ -16238,7 +16347,7 @@ index be5339e..8352c93 100644 /* * Issue the commands to select the specified schema as the current schema -@@ -2827,9 +2901,12 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat +@@ -2829,9 +2903,12 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat _selectOutputSchema(AH, te->namespace); _selectTablespace(AH, te->tablespace); @@ -16253,7 +16362,7 @@ index be5339e..8352c93 100644 /* Emit header comment for item */ if (!AH->noTocComments) diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h -index 0a135ee..27b2ef5 100644 +index 9f826b6..a46be6b 100644 --- a/src/bin/pg_dump/pg_backup_archiver.h +++ b/src/bin/pg_dump/pg_backup_archiver.h @@ -63,7 +63,7 @@ typedef z_stream *z_streamp; @@ -18077,7 +18186,7 @@ index 6047735..ce9a994 100644 #endif /* SYSATTR_H */ diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h -index e38a6e76..d5688d5 100644 +index e38a6e7..d5688d5 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -75,13 +75,14 @@ typedef struct tupleDesc @@ -18508,7 +18617,7 @@ index 4dc2d4f..e50a83f 100644 #endif /* TYPECMDS_H */ diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h -index b2424a0..609f31b 100644 +index 13b6aa5..0350a17 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -131,8 +131,8 @@ extern TupleHashEntry FindTupleHashEntry(TupleHashTable hashtable, @@ -18522,15 +18631,15 @@ index b2424a0..609f31b 100644 extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, TupleDesc cleanTupType, TupleTableSlot *slot); -@@ -164,6 +164,7 @@ extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, +@@ -166,6 +166,7 @@ extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, int instrument_options); extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); +extern bool ExecContextForcesSecids(PlanState *planstate, bool *hassecid); extern void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate); - extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate, -@@ -234,8 +235,8 @@ extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate); + extern ExecRowMark *ExecFindRowMark(EState *estate, Index rti); +@@ -238,8 +239,8 @@ extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate); extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, TupleDesc tupType); @@ -18608,7 +18717,7 @@ index 54eebbc..94d0969 100644 /* ---------------------- * Create Rule Statement diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h -index 5065bd6..0c67e25 100644 +index ad835d9..71595e3 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -208,6 +208,7 @@ PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD) @@ -19323,10 +19432,10 @@ index 9faefbe..90b7c94 100644 SearchSysCacheList(cacheId, 1, key1, 0, 0, 0) #define SearchSysCacheList2(cacheId, key1, key2) \ diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c -index 25d2760..9bb9d3f 100644 +index 589d514..5370346 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c -@@ -1936,7 +1936,7 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars) +@@ -1965,7 +1965,7 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars) row = palloc0(sizeof(PLpgSQL_row)); row->dtype = PLPGSQL_DTYPE_ROW; diff --git a/sepostgresql.spec b/sepostgresql.spec index 2e823f2..6c40640 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -11,8 +11,8 @@ Summary: Security Enhanced PostgreSQL Name: sepostgresql -Version: 9.0.1 -Release: 20101008%{?dist} +Version: 9.0.3 +Release: 20110415%{?dist} License: PostgreSQL Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ @@ -24,7 +24,7 @@ Source3: sepostgresql.logrotate Patch0: sepostgresql-fedora-prefix.patch Patch1: sepostgresql-9.0-fullset.patch BuildRequires: perl glibc-devel bison flex readline-devel zlib-devel >= 1.0.4 -BuildRequires: checkpolicy libselinux-devel >= 2.0.80 audit-libs-devel +BuildRequires: checkpolicy libselinux-devel >= 2.0.96 audit-libs-devel BuildRequires: selinux-policy >= 3.6.8 %if %{ssl} BuildRequires: openssl-devel @@ -33,7 +33,7 @@ Requires(pre): shadow-utils Requires(post): policycoreutils /sbin/chkconfig Requires(preun): /sbin/chkconfig /sbin/service Requires(postun): policycoreutils -Requires: policycoreutils >= 2.0.16 libselinux >= 2.0.80 +Requires: policycoreutils >= 2.0.16 libselinux >= 2.0.96 Requires: selinux-policy >= 3.6.8 Requires: tzdata logrotate @@ -181,6 +181,10 @@ fi %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog +* Fri Apr 15 2011 KaiGai Kohei - 9.0.3-20110415 +- upgrade base version to 9.0.3 +- initial labeling logic was revised to use selabel_lookup() + * Wed Feb 09 2011 Fedora Release Engineering - 9.0.1-20101008 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild diff --git a/sources b/sources index 5a1101a..8ba5714 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -5093c321bc47af2ea9afa726605ff1ce postgresql-9.0.1.tar.gz +56386ded2d5dcd8a4ceef0da81c3d22c postgresql-9.0.3.tar.gz From 3506cc8b8054d1c56042cb8536ec7726a3197971 Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Fri, 13 Jan 2012 21:49:53 -0600 Subject: [PATCH 23/24] - Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild --- sepostgresql.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sepostgresql.spec b/sepostgresql.spec index 6c40640..dc6125e 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -12,7 +12,7 @@ Summary: Security Enhanced PostgreSQL Name: sepostgresql Version: 9.0.3 -Release: 20110415%{?dist} +Release: 20110416%{?dist} License: PostgreSQL Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ @@ -181,6 +181,9 @@ fi %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog +* Sat Jan 14 2012 Fedora Release Engineering - 9.0.3-20110416 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + * Fri Apr 15 2011 KaiGai Kohei - 9.0.3-20110415 - upgrade base version to 9.0.3 - initial labeling logic was revised to use selabel_lookup() From 13d348f3358282b48f1165294eaa6a2440d5d2a4 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Tue, 17 Apr 2012 19:05:13 +0200 Subject: [PATCH 24/24] SE-PostgreSQL was merged into mainstream as contrib/sepgsql extension --- .gitignore | 4 - dead.package | 1 + sepostgresql-9.0-fullset.patch | 19467 ----------------------------- sepostgresql-fedora-prefix.patch | 105 - sepostgresql.8 | 137 - sepostgresql.init | 205 - sepostgresql.logrotate | 11 - sepostgresql.spec | 485 - sources | 1 - 9 files changed, 1 insertion(+), 20415 deletions(-) delete mode 100644 .gitignore create mode 100644 dead.package delete mode 100644 sepostgresql-9.0-fullset.patch delete mode 100644 sepostgresql-fedora-prefix.patch delete mode 100644 sepostgresql.8 delete mode 100644 sepostgresql.init delete mode 100644 sepostgresql.logrotate delete mode 100644 sepostgresql.spec delete mode 100644 sources diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 81ff717..0000000 --- a/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -postgresql-9.0beta1.tar.gz -/postgresql-9.0.0.tar.gz -/postgresql-9.0.1.tar.gz -/postgresql-9.0.3.tar.gz diff --git a/dead.package b/dead.package new file mode 100644 index 0000000..e1c0fac --- /dev/null +++ b/dead.package @@ -0,0 +1 @@ +SE-PostgreSQL was merged into mainstream as contrib/sepgsql extension diff --git a/sepostgresql-9.0-fullset.patch b/sepostgresql-9.0-fullset.patch deleted file mode 100644 index e4b3c26..0000000 --- a/sepostgresql-9.0-fullset.patch +++ /dev/null @@ -1,19467 +0,0 @@ - configure | 198 ++++++ - configure.in | 13 + - contrib/adminpack/adminpack.c | 28 +- - src/Makefile.global.in | 1 + - src/backend/Makefile | 7 +- - src/backend/access/common/heaptuple.c | 14 + - src/backend/access/common/tupdesc.c | 20 +- - src/backend/access/gin/ginutil.c | 2 +- - src/backend/access/heap/heapam.c | 35 + - src/backend/access/heap/tuptoaster.c | 6 + - src/backend/access/transam/twophase.c | 2 +- - src/backend/access/transam/xact.c | 19 + - src/backend/access/transam/xlog.c | 2 +- - src/backend/bootstrap/bootparse.y | 7 +- - src/backend/bootstrap/bootstrap.c | 14 + - src/backend/catalog/Makefile | 4 +- - src/backend/catalog/aclchk.c | 20 + - src/backend/catalog/catalog.c | 6 + - src/backend/catalog/genbki.pl | 3 +- - src/backend/catalog/heap.c | 120 +++- - src/backend/catalog/index.c | 28 +- - src/backend/catalog/namespace.c | 26 +- - src/backend/catalog/pg_aggregate.c | 9 +- - src/backend/catalog/pg_conversion.c | 4 +- - src/backend/catalog/pg_largeobject.c | 65 ++- - src/backend/catalog/pg_namespace.c | 4 +- - src/backend/catalog/pg_operator.c | 21 + - src/backend/catalog/pg_proc.c | 8 +- - src/backend/catalog/pg_seclabel.c | 646 +++++++++++++++++++ - src/backend/catalog/pg_type.c | 17 +- - src/backend/catalog/toasting.c | 12 +- - src/backend/commands/aggregatecmds.c | 4 + - src/backend/commands/alter.c | 61 ++ - src/backend/commands/cluster.c | 23 +- - src/backend/commands/comment.c | 60 ++ - src/backend/commands/conversioncmds.c | 16 +- - src/backend/commands/copy.c | 265 +++++++-- - src/backend/commands/dbcommands.c | 87 +++ - src/backend/commands/explain.c | 2 +- - src/backend/commands/foreigncmds.c | 39 ++ - src/backend/commands/functioncmds.c | 82 +++- - src/backend/commands/indexcmds.c | 18 +- - src/backend/commands/lockcmds.c | 4 + - src/backend/commands/opclasscmds.c | 48 ++- - src/backend/commands/operatorcmds.c | 6 + - src/backend/commands/prepare.c | 2 +- - src/backend/commands/proclang.c | 10 +- - src/backend/commands/schemacmds.c | 57 ++- - src/backend/commands/sequence.c | 16 + - src/backend/commands/tablecmds.c | 509 +++++++++++++++- - src/backend/commands/tablespace.c | 64 ++ - src/backend/commands/trigger.c | 11 + - src/backend/commands/tsearchcmds.c | 64 ++- - src/backend/commands/typecmds.c | 159 +++++- - src/backend/commands/user.c | 20 + - src/backend/commands/vacuum.c | 3 + - src/backend/commands/view.c | 4 + - src/backend/executor/execJunk.c | 5 +- - src/backend/executor/execMain.c | 63 ++- - src/backend/executor/execQual.c | 11 +- - src/backend/executor/execScan.c | 23 +- - src/backend/executor/execTuples.c | 17 +- - src/backend/executor/execUtils.c | 6 +- - src/backend/executor/functions.c | 6 +- - src/backend/executor/nodeAgg.c | 5 +- - src/backend/executor/nodeFunctionscan.c | 2 +- - src/backend/executor/nodeMergejoin.c | 4 + - src/backend/executor/nodeModifyTable.c | 76 +++- - src/backend/executor/nodeSubplan.c | 4 +- - src/backend/executor/nodeWindowAgg.c | 7 + - src/backend/executor/spi.c | 13 +- - src/backend/libpq/be-fsstubs.c | 40 +- - src/backend/nodes/copyfuncs.c | 19 + - src/backend/nodes/equalfuncs.c | 17 + - src/backend/nodes/outfuncs.c | 1 + - src/backend/nodes/readfuncs.c | 1 + - src/backend/optimizer/plan/planner.c | 11 + - src/backend/optimizer/util/clauses.c | 8 +- - src/backend/parser/analyze.c | 4 +- - src/backend/parser/gram.y | 120 ++++- - src/backend/parser/parse_relation.c | 12 +- - src/backend/parser/parse_target.c | 75 ++- - src/backend/parser/parse_utilcmd.c | 10 +- - src/backend/postmaster/postmaster.c | 45 ++- - src/backend/rewrite/rewriteDefine.c | 4 + - src/backend/rewrite/rewriteRemove.c | 4 + - src/backend/sepgsql/Makefile | 21 + - src/backend/sepgsql/attribute.c | 260 ++++++++ - src/backend/sepgsql/avc.c | 503 +++++++++++++++ - src/backend/sepgsql/blob.c | 245 ++++++++ - src/backend/sepgsql/conversion.c | 147 +++++ - src/backend/sepgsql/database.c | 201 ++++++ - src/backend/sepgsql/fdw.c | 296 +++++++++ - src/backend/sepgsql/file.c | 287 +++++++++ - src/backend/sepgsql/label.c | 588 ++++++++++++++++++ - src/backend/sepgsql/misc.c | 147 +++++ - src/backend/sepgsql/operator.c | 454 ++++++++++++++ - src/backend/sepgsql/proc.c | 366 +++++++++++ - src/backend/sepgsql/relation.c | 819 +++++++++++++++++++++++++ - src/backend/sepgsql/role.c | 142 +++++ - src/backend/sepgsql/rowlv.c | 346 +++++++++++ - src/backend/sepgsql/schema.c | 173 ++++++ - src/backend/sepgsql/selinux.c | 690 +++++++++++++++++++++ - src/backend/sepgsql/tablespace.c | 157 +++++ - src/backend/sepgsql/tsearch.c | 524 ++++++++++++++++ - src/backend/sepgsql/type.c | 314 ++++++++++ - src/backend/storage/large_object/inv_api.c | 4 +- - src/backend/tcop/fastpath.c | 5 + - src/backend/tcop/pquery.c | 2 +- - src/backend/tcop/utility.c | 49 ++ - src/backend/tsearch/wparser.c | 4 +- - src/backend/utils/adt/acl.c | 2 +- - src/backend/utils/adt/datetime.c | 4 +- - src/backend/utils/adt/dbsize.c | 6 + - src/backend/utils/adt/genfile.c | 9 +- - src/backend/utils/adt/lockfuncs.c | 2 +- - src/backend/utils/adt/misc.c | 2 +- - src/backend/utils/adt/pgstatfuncs.c | 2 +- - src/backend/utils/adt/ri_triggers.c | 24 + - src/backend/utils/adt/tid.c | 5 + - src/backend/utils/adt/trigfuncs.c | 4 + - src/backend/utils/adt/tsvector_op.c | 2 +- - src/backend/utils/cache/plancache.c | 8 +- - src/backend/utils/cache/relcache.c | 38 +- - src/backend/utils/cache/syscache.c | 24 + - src/backend/utils/fmgr/fmgr.c | 32 +- - src/backend/utils/fmgr/funcapi.c | 4 +- - src/backend/utils/init/postinit.c | 7 + - src/backend/utils/misc/guc.c | 74 ++- - src/backend/utils/misc/postgresql.conf.sample | 2 + - src/backend/utils/mmgr/portalmem.c | 2 +- - src/bin/initdb/initdb.c | 13 + - src/bin/pg_dump/pg_backup.h | 3 +- - src/bin/pg_dump/pg_backup_archiver.c | 81 +++- - src/bin/pg_dump/pg_backup_archiver.h | 9 +- - src/bin/pg_dump/pg_dump.c | 400 ++++++++++-- - src/bin/pg_dump/pg_dump.h | 7 + - src/bin/pg_dump/pg_dumpall.c | 60 ++- - src/bin/pg_dump/pg_restore.c | 6 + - src/include/access/htup.h | 35 +- - src/include/access/sysattr.h | 3 +- - src/include/access/tupdesc.h | 5 +- - src/include/bootstrap/bootstrap.h | 1 + - src/include/catalog/heap.h | 16 +- - src/include/catalog/indexing.h | 5 + - src/include/catalog/pg_class.h | 28 +- - src/include/catalog/pg_conversion_fn.h | 2 +- - src/include/catalog/pg_largeobject.h | 3 +- - src/include/catalog/pg_namespace.h | 2 +- - src/include/catalog/pg_proc.h | 4 + - src/include/catalog/pg_proc_fn.h | 3 +- - src/include/catalog/pg_seclabel.h | 79 +++ - src/include/catalog/pg_type_fn.h | 3 +- - src/include/catalog/toasting.h | 3 + - src/include/commands/alter.h | 1 + - src/include/commands/dbcommands.h | 1 + - src/include/commands/defrem.h | 2 + - src/include/commands/schemacmds.h | 1 + - src/include/commands/tablecmds.h | 7 + - src/include/commands/tablespace.h | 1 + - src/include/commands/typecmds.h | 2 + - src/include/executor/executor.h | 9 +- - src/include/fmgr.h | 1 + - src/include/nodes/nodes.h | 1 + - src/include/nodes/parsenodes.h | 17 + - src/include/parser/kwlist.h | 1 + - src/include/pg_config.h.in | 3 + - src/include/sepgsql/hooks.h | 293 +++++++++ - src/include/sepgsql/sepgsql.h | 318 ++++++++++ - src/include/storage/large_object.h | 2 +- - src/include/storage/lwlock.h | 1 + - src/include/utils/guc.h | 1 + - src/include/utils/syscache.h | 11 + - src/pl/plpgsql/src/pl_comp.c | 2 +- - src/test/regress/expected/sanity_check.out | 3 +- - 175 files changed, 11679 insertions(+), 375 deletions(-) - -diff --git a/configure b/configure -index a4f4c9e..e8e71c1 100755 ---- a/configure -+++ b/configure -@@ -708,6 +708,7 @@ LDFLAGS_EX - ELF_SYS - EGREP - GREP -+enable_selinux - with_zlib - with_system_tzdata - with_libxslt -@@ -843,6 +844,7 @@ with_libxml - with_libxslt - with_system_tzdata - with_zlib -+enable_selinux - with_gnu_ld - enable_largefile - enable_float4_byval -@@ -1500,6 +1502,7 @@ Optional Features: - --enable-depend turn on automatic dependency tracking - --enable-cassert enable assertion checks (for debugging) - --disable-thread-safety disable thread-safety in client libraries -+ --enable-selinux build with SELinux support - --disable-largefile omit support for large files - --disable-float4-byval disable float4 passed by value - --disable-float8-byval disable float8 passed by value -@@ -5611,6 +5614,201 @@ fi - - - # -+# SELinux support -+# -+ -+ -+# Check whether --enable-selinux was given. -+if test "${enable_selinux+set}" = set; then -+ enableval=$enable_selinux; -+ case $enableval in -+ yes) -+ : -+ ;; -+ no) -+ : -+ ;; -+ *) -+ { { $as_echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5 -+$as_echo "$as_me: error: no argument expected for --enable-selinux option" >&2;} -+ { (exit 1); exit 1; }; } -+ ;; -+ esac -+ -+else -+ enable_selinux=no -+ -+fi -+ -+ -+if test "$enable_selinux" = yes; then -+ -+{ $as_echo "$as_me:$LINENO: checking for avc_open in -lselinux" >&5 -+$as_echo_n "checking for avc_open in -lselinux... " >&6; } -+if test "${ac_cv_lib_selinux_avc_open+set}" = set; then -+ $as_echo_n "(cached) " >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-lselinux $LIBS" -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char avc_open (); -+int -+main () -+{ -+return avc_open (); -+ ; -+ return 0; -+} -+_ACEOF -+rm -f conftest.$ac_objext conftest$ac_exeext -+if { (ac_try="$ac_link" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" -+$as_echo "$ac_try_echo") >&5 -+ (eval "$ac_link") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest$ac_exeext && { -+ test "$cross_compiling" = yes || -+ $as_test_x conftest$ac_exeext -+ }; then -+ ac_cv_lib_selinux_avc_open=yes -+else -+ $as_echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_cv_lib_selinux_avc_open=no -+fi -+ -+rm -rf conftest.dSYM -+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_avc_open" >&5 -+$as_echo "$ac_cv_lib_selinux_avc_open" >&6; } -+if test "x$ac_cv_lib_selinux_avc_open" = x""yes; then -+ cat >>confdefs.h <<_ACEOF -+#define HAVE_LIBSELINUX 1 -+_ACEOF -+ -+ LIBS="-lselinux $LIBS" -+ -+else -+ { { $as_echo "$as_me:$LINENO: error: \"SELinux support requires libselinux.\"" >&5 -+$as_echo "$as_me: error: \"SELinux support requires libselinux.\"" >&2;} -+ { (exit 1); exit 1; }; } -+fi -+ -+ -+{ $as_echo "$as_me:$LINENO: checking for audit_open in -laudit" >&5 -+$as_echo_n "checking for audit_open in -laudit... " >&6; } -+if test "${ac_cv_lib_audit_audit_open+set}" = set; then -+ $as_echo_n "(cached) " >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-laudit $LIBS" -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char audit_open (); -+int -+main () -+{ -+return audit_open (); -+ ; -+ return 0; -+} -+_ACEOF -+rm -f conftest.$ac_objext conftest$ac_exeext -+if { (ac_try="$ac_link" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" -+$as_echo "$ac_try_echo") >&5 -+ (eval "$ac_link") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest$ac_exeext && { -+ test "$cross_compiling" = yes || -+ $as_test_x conftest$ac_exeext -+ }; then -+ ac_cv_lib_audit_audit_open=yes -+else -+ $as_echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_cv_lib_audit_audit_open=no -+fi -+ -+rm -rf conftest.dSYM -+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_audit_audit_open" >&5 -+$as_echo "$ac_cv_lib_audit_audit_open" >&6; } -+if test "x$ac_cv_lib_audit_audit_open" = x""yes; then -+ cat >>confdefs.h <<_ACEOF -+#define HAVE_LIBAUDIT 1 -+_ACEOF -+ -+ LIBS="-laudit $LIBS" -+ -+else -+ { { $as_echo "$as_me:$LINENO: error: \"SELinux support requires libaudit.\"" >&5 -+$as_echo "$as_me: error: \"SELinux support requires libaudit.\"" >&2;} -+ { (exit 1); exit 1; }; } -+fi -+ -+ -+cat >>confdefs.h <<_ACEOF -+#define HAVE_SELINUX 1 -+_ACEOF -+ -+ -+fi -+ -+# - # Elf - # - -diff --git a/configure.in b/configure.in -index 26166d0..0a58c40 100644 ---- a/configure.in -+++ b/configure.in -@@ -755,6 +755,19 @@ PGAC_ARG_BOOL(with, zlib, yes, - AC_SUBST(with_zlib) - - # -+# SELinux support -+# -+PGAC_ARG_BOOL(enable, selinux, no, [build with SELinux support]) -+if test "$enable_selinux" = yes; then -+ AC_CHECK_LIB(selinux, avc_open,, -+ AC_MSG_ERROR("SELinux support requires libselinux.")) -+ AC_CHECK_LIB(audit, audit_open,, -+ AC_MSG_ERROR("SELinux support requires libaudit.")) -+ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, [SE-PostgreSQL feature is enabled]) -+ AC_SUBST(enable_selinux) -+fi -+ -+# - # Elf - # - -diff --git a/contrib/adminpack/adminpack.c b/contrib/adminpack/adminpack.c -index 5271ef6..397786d 100644 ---- a/contrib/adminpack/adminpack.c -+++ b/contrib/adminpack/adminpack.c -@@ -22,6 +22,7 @@ - #include "funcapi.h" - #include "miscadmin.h" - #include "postmaster/syslogger.h" -+#include "sepgsql/hooks.h" - #include "storage/fd.h" - #include "utils/builtins.h" - #include "utils/datetime.h" -@@ -142,10 +143,16 @@ pg_file_write(PG_FUNCTION_ARGS) - (ERRCODE_DUPLICATE_FILE, - errmsg("file \"%s\" exists", filename))); - -+ sepgsql_file_write(filename, true); -+ - f = fopen(filename, "wb"); - } - else -+ { -+ sepgsql_file_append(filename); -+ - f = fopen(filename, "ab"); -+ } - - if (!f) - ereport(ERROR, -@@ -217,6 +224,7 @@ pg_file_rename(PG_FUNCTION_ARGS) - - if (fn3) - { -+ sepgsql_file_rename(fn2, fn3); - if (rename(fn2, fn3) != 0) - { - ereport(ERROR, -@@ -224,6 +232,7 @@ pg_file_rename(PG_FUNCTION_ARGS) - errmsg("could not rename \"%s\" to \"%s\": %m", - fn2, fn3))); - } -+ sepgsql_file_rename(fn1, fn2); - if (rename(fn1, fn2) != 0) - { - ereport(WARNING, -@@ -247,11 +256,15 @@ pg_file_rename(PG_FUNCTION_ARGS) - } - } - } -- else if (rename(fn1, fn2) != 0) -+ else - { -- ereport(ERROR, -- (errcode_for_file_access(), -- errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2))); -+ sepgsql_file_rename(fn1, fn2); -+ if (rename(fn1, fn2) != 0) -+ { -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2))); -+ } - } - - PG_RETURN_BOOL(true); -@@ -277,6 +290,8 @@ pg_file_unlink(PG_FUNCTION_ARGS) - errmsg("file \"%s\" is not accessible: %m", filename))); - } - -+ sepgsql_file_unlink(filename); -+ - if (unlink(filename) < 0) - { - ereport(WARNING, -@@ -312,11 +327,14 @@ pg_logdir_ls(PG_FUNCTION_ARGS) - TupleDesc tupdesc; - - funcctx = SRF_FIRSTCALL_INIT(); -+ -+ sepgsql_dir_list(Log_directory); -+ - oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - - fctx = palloc(sizeof(directory_fctx)); - -- tupdesc = CreateTemplateTupleDesc(2, false); -+ tupdesc = CreateTemplateTupleDesc(2, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime", - TIMESTAMPOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename", -diff --git a/src/Makefile.global.in b/src/Makefile.global.in -index 280578a..6495097 100644 ---- a/src/Makefile.global.in -+++ b/src/Makefile.global.in -@@ -166,6 +166,7 @@ enable_nls = @enable_nls@ - enable_debug = @enable_debug@ - enable_dtrace = @enable_dtrace@ - enable_coverage = @enable_coverage@ -+enable_selinux = @enable_selinux@ - enable_thread_safety = @enable_thread_safety@ - - python_includespec = @python_includespec@ -diff --git a/src/backend/Makefile b/src/backend/Makefile -index a11b2b5..25198fd 100644 ---- a/src/backend/Makefile -+++ b/src/backend/Makefile -@@ -19,7 +19,7 @@ include $(top_builddir)/src/Makefile.global - - SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ - main nodes optimizer port postmaster regex replication rewrite \ -- storage tcop tsearch utils $(top_builddir)/src/timezone -+ sepgsql storage tcop tsearch utils $(top_builddir)/src/timezone - - include $(srcdir)/common.mk - -@@ -43,6 +43,11 @@ LIBS := $(filter-out -lpgport, $(LIBS)) $(LDAP_LIBS_BE) - # The backend doesn't need everything that's in LIBS, however - LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) - -+# SELinux needs libselinux and libaudit -+ifeq ($(enable_selinux), yes) -+LIBS := $(filter-out -lselinux -laudit, $(LIBS)) -lselinux -laudit -+endif -+ - ########################################################################## - - all: submake-libpgport submake-schemapg postgres $(POSTGRES_IMP) -diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c -index 8d770a3..a3b1474 100644 ---- a/src/backend/access/common/heaptuple.c -+++ b/src/backend/access/common/heaptuple.c -@@ -60,6 +60,7 @@ - #include "access/heapam.h" - #include "access/sysattr.h" - #include "access/tuptoaster.h" -+#include "catalog/pg_seclabel.h" - #include "executor/tuptable.h" - - -@@ -559,6 +560,9 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) - case TableOidAttributeNumber: - result = ObjectIdGetDatum(tup->t_tableOid); - break; -+ case SecurityLabelAttributeNumber: -+ result = seclabelSysattOutput(tup->t_tableOid, tup); -+ break; - default: - elog(ERROR, "invalid attnum: %d", attnum); - result = 0; /* keep compiler quiet */ -@@ -682,6 +686,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, - - if (tupleDescriptor->tdhasoid) - len += sizeof(Oid); -+ if (tupleDescriptor->tdhassecid) -+ len += sizeof(Oid)+1; - - hoff = len = MAXALIGN(len); /* align user data safely */ - -@@ -713,6 +719,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, - - if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ - td->t_infomask = HEAP_HASOID; -+ if (tupleDescriptor->tdhassecid) -+ td->t_infomask |= HEAP_HASSECID; - - heap_fill_tuple(tupleDescriptor, - values, -@@ -824,6 +832,8 @@ heap_modify_tuple(HeapTuple tuple, - newTuple->t_tableOid = tuple->t_tableOid; - if (tupleDesc->tdhasoid) - HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); -+ if (tupleDesc->tdhassecid) -+ HeapTupleSetSecid(newTuple, HeapTupleGetSecid(tuple)); - - return newTuple; - } -@@ -1434,6 +1444,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor, - - if (tupleDescriptor->tdhasoid) - len += sizeof(Oid); -+ if (tupleDescriptor->tdhassecid) -+ len += sizeof(Oid)+1; - - hoff = len = MAXALIGN(len); /* align user data safely */ - -@@ -1455,6 +1467,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor, - - if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ - tuple->t_infomask = HEAP_HASOID; -+ if (tupleDescriptor->tdhassecid) -+ tuple->t_infomask |= HEAP_HASSECID; - - heap_fill_tuple(tupleDescriptor, - values, -diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c -index 2125fdf..5880ec7 100644 ---- a/src/backend/access/common/tupdesc.c -+++ b/src/backend/access/common/tupdesc.c -@@ -34,7 +34,7 @@ - * caller can overwrite this if needed. - */ - TupleDesc --CreateTemplateTupleDesc(int natts, bool hasoid) -+CreateTemplateTupleDesc(int natts, bool hasoid, bool hassecid) - { - TupleDesc desc; - char *stg; -@@ -88,6 +88,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid) - desc->tdtypeid = RECORDOID; - desc->tdtypmod = -1; - desc->tdhasoid = hasoid; -+ desc->tdhassecid = hassecid; - desc->tdrefcount = -1; /* assume not reference-counted */ - - return desc; -@@ -105,7 +106,8 @@ CreateTemplateTupleDesc(int natts, bool hasoid) - * caller can overwrite this if needed. - */ - TupleDesc --CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs) -+CreateTupleDesc(int natts, bool hasoid, bool hassecid, -+ Form_pg_attribute *attrs) - { - TupleDesc desc; - -@@ -121,6 +123,7 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs) - desc->tdtypeid = RECORDOID; - desc->tdtypmod = -1; - desc->tdhasoid = hasoid; -+ desc->tdhassecid = hassecid; - desc->tdrefcount = -1; /* assume not reference-counted */ - - return desc; -@@ -139,7 +142,8 @@ CreateTupleDescCopy(TupleDesc tupdesc) - TupleDesc desc; - int i; - -- desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid); -+ desc = CreateTemplateTupleDesc(tupdesc->natts, -+ tupdesc->tdhasoid, tupdesc->tdhassecid); - - for (i = 0; i < desc->natts; i++) - { -@@ -150,6 +154,7 @@ CreateTupleDescCopy(TupleDesc tupdesc) - - desc->tdtypeid = tupdesc->tdtypeid; - desc->tdtypmod = tupdesc->tdtypmod; -+ desc->tdhassecid = tupdesc->tdhassecid; - - return desc; - } -@@ -166,7 +171,8 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) - TupleConstr *constr = tupdesc->constr; - int i; - -- desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid); -+ desc = CreateTemplateTupleDesc(tupdesc->natts, -+ tupdesc->tdhasoid, tupdesc->tdhassecid); - - for (i = 0; i < desc->natts; i++) - { -@@ -314,6 +320,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) - return false; - if (tupdesc1->tdhasoid != tupdesc2->tdhasoid) - return false; -+ if (tupdesc1->tdhassecid != tupdesc2->tdhassecid) -+ return false; - - for (i = 0; i < tupdesc1->natts; i++) - { -@@ -519,7 +527,7 @@ BuildDescForRelation(List *schema) - * allocate a new tuple descriptor - */ - natts = list_length(schema); -- desc = CreateTemplateTupleDesc(natts, false); -+ desc = CreateTemplateTupleDesc(natts, false, false); - has_not_null = false; - - attnum = 0; -@@ -604,7 +612,7 @@ BuildDescFromLists(List *names, List *types, List *typmods) - /* - * allocate a new tuple descriptor - */ -- desc = CreateTemplateTupleDesc(natts, false); -+ desc = CreateTemplateTupleDesc(natts, false, false); - - attnum = 0; - -diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c -index 2a75820..065ba1a 100644 ---- a/src/backend/access/gin/ginutil.c -+++ b/src/backend/access/gin/ginutil.c -@@ -33,7 +33,7 @@ initGinState(GinState *state, Relation index) - - for (i = 0; i < index->rd_att->natts; i++) - { -- state->tupdesc[i] = CreateTemplateTupleDesc(2, false); -+ state->tupdesc[i] = CreateTemplateTupleDesc(2, false, false); - - TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL, - INT2OID, -1, 0); -diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c -index 1c93400..1236873 100644 ---- a/src/backend/access/heap/heapam.c -+++ b/src/backend/access/heap/heapam.c -@@ -52,6 +52,7 @@ - #include "access/xlogutils.h" - #include "catalog/catalog.h" - #include "catalog/namespace.h" -+#include "catalog/pg_seclabel.h" - #include "miscadmin.h" - #include "pgstat.h" - #include "storage/bufmgr.h" -@@ -1862,6 +1863,22 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid, - Assert(!(tup->t_data->t_infomask & HEAP_HASOID)); - } - -+ /* -+ * If this tuple has a capability to store its security id, but it has -+ * not been assigned yet, the default security id should be assigned. -+ * Note that this step does not apply any permission checks. All the -+ * caller of heap_insert() is trusted. -+ */ -+ if (relation->rd_rel->relhassecids) -+ { -+ if (!OidIsValid(HeapTupleGetSecid(tup))) -+ HeapTupleSetSecid(tup, seclabelGetNewSecid(relation, tup)); -+ } -+ else -+ { -+ Assert(!HeapTupleHasSecid(tup)); -+ } -+ - tup->t_data->t_infomask &= ~(HEAP_XACT_MASK); - tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); - tup->t_data->t_infomask |= HEAP_XMAX_INVALID; -@@ -2560,6 +2577,20 @@ l2: - Assert(!(newtup->t_data->t_infomask & HEAP_HASOID)); - } - -+ /* -+ * Preserve security-id, if not changed -+ */ -+ if (relation->rd_rel->relhassecids) -+ { -+ if (!OidIsValid(HeapTupleGetSecid(newtup))) -+ HeapTupleSetSecid(newtup, HeapTupleGetSecid(&oldtup)); -+ } -+ else -+ { -+ /* check there is not space for a security-id */ -+ Assert(!HeapTupleHasSecid(newtup)); -+ } -+ - newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK); - newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); - newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED); -@@ -3501,6 +3532,10 @@ heap_inplace_update(Relation relation, HeapTuple tuple) - memcpy((char *) htup + htup->t_hoff, - (char *) tuple->t_data + tuple->t_data->t_hoff, - newlen); -+ if (HeapTupleHeaderHasOid(htup)) -+ HeapTupleHeaderSetOid(htup, HeapTupleGetOid(tuple)); -+ if (HeapTupleHeaderHasSecid(htup)) -+ HeapTupleHeaderSetSecid(htup, HeapTupleGetSecid(tuple)); - - MarkBufferDirty(buffer); - -diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c -index 7518db1..fb78993 100644 ---- a/src/backend/access/heap/tuptoaster.c -+++ b/src/backend/access/heap/tuptoaster.c -@@ -591,6 +591,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, - hoff += BITMAPLEN(numAttrs); - if (newtup->t_data->t_infomask & HEAP_HASOID) - hoff += sizeof(Oid); -+ if (HeapTupleHasSecid(newtup)) -+ hoff += sizeof(Oid); - hoff = MAXALIGN(hoff); - Assert(hoff == newtup->t_data->t_hoff); - /* now convert to a limit on the tuple data size */ -@@ -868,6 +870,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, - new_len += BITMAPLEN(numAttrs); - if (olddata->t_infomask & HEAP_HASOID) - new_len += sizeof(Oid); -+ if (HeapTupleHeaderHasSecid(olddata)) -+ new_len += sizeof(Oid); - new_len = MAXALIGN(new_len); - Assert(new_len == olddata->t_hoff); - new_data_len = heap_compute_data_size(tupleDesc, -@@ -1019,6 +1023,8 @@ toast_flatten_tuple_attribute(Datum value, - new_len += BITMAPLEN(numAttrs); - if (olddata->t_infomask & HEAP_HASOID) - new_len += sizeof(Oid); -+ if (HeapTupleHeaderHasSecid(olddata)) -+ new_len += sizeof(Oid)+1; - new_len = MAXALIGN(new_len); - Assert(new_len == olddata->t_hoff); - new_data_len = heap_compute_data_size(tupleDesc, -diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c -index d6dca97..710b891 100644 ---- a/src/backend/access/transam/twophase.c -+++ b/src/backend/access/transam/twophase.c -@@ -605,7 +605,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS) - - /* build tupdesc for result tuples */ - /* this had better match pg_prepared_xacts view in system_views.sql */ -- tupdesc = CreateTemplateTupleDesc(5, false); -+ tupdesc = CreateTemplateTupleDesc(5, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "transaction", - XIDOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "gid", -diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c -index 8436dea..d9de0e4 100644 ---- a/src/backend/access/transam/xact.c -+++ b/src/backend/access/transam/xact.c -@@ -36,6 +36,7 @@ - #include "libpq/be-fsstubs.h" - #include "miscadmin.h" - #include "pgstat.h" -+#include "sepgsql/hooks.h" - #include "storage/bufmgr.h" - #include "storage/fd.h" - #include "storage/lmgr.h" -@@ -140,6 +141,8 @@ typedef struct TransactionStateData - int maxChildXids; /* allocated size of childXids[] */ - Oid prevUser; /* previous CurrentUserId setting */ - int prevSecContext; /* previous SecurityRestrictionContext */ -+ char *prevSecLabel; /* previous security label of client */ -+ int prevRowlvMode; /* previous row-level access control mode */ - bool prevXactReadOnly; /* entry-time xact r/o state */ - bool startedInRecovery; /* did we start in recovery? */ - struct TransactionStateData *parent; /* back link to parent */ -@@ -169,6 +172,8 @@ static TransactionStateData TopTransactionStateData = { - 0, /* allocated size of childXids[] */ - InvalidOid, /* previous CurrentUserId setting */ - 0, /* previous SecurityRestrictionContext */ -+ NULL, /* previous security label of the client */ -+ 0, /* previous row-level access control mode */ - false, /* entry-time xact r/o state */ - false, /* startedInRecovery */ - NULL /* link to parent state block */ -@@ -1698,6 +1703,10 @@ StartTransaction(void) - /* SecurityRestrictionContext should never be set outside a transaction */ - Assert(s->prevSecContext == 0); - -+ /* Save status of SELinux */ -+ s->prevSecLabel = sepgsql_get_client_label(); -+ s->prevRowlvMode = sepgsql_rowlv_get_mode(); -+ - /* - * initialize other subsystems for new transaction - */ -@@ -2202,6 +2211,10 @@ AbortTransaction(void) - */ - SetUserIdAndSecContext(s->prevUser, s->prevSecContext); - -+ /* Reset SELinux status */ -+ sepgsql_set_client_label(s->prevSecLabel); -+ sepgsql_rowlv_set_mode(s->prevRowlvMode); -+ - /* - * do abort processing - */ -@@ -4046,6 +4059,10 @@ AbortSubTransaction(void) - */ - SetUserIdAndSecContext(s->prevUser, s->prevSecContext); - -+ /* Reset SELinux status */ -+ sepgsql_set_client_label(s->prevSecLabel); -+ sepgsql_rowlv_set_mode(s->prevRowlvMode); -+ - /* - * We can skip all this stuff if the subxact failed before creating a - * ResourceOwner... -@@ -4185,6 +4202,8 @@ PushTransaction(void) - s->state = TRANS_DEFAULT; - s->blockState = TBLOCK_SUBBEGIN; - GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); -+ s->prevSecLabel = sepgsql_get_client_label(); -+ s->prevRowlvMode = sepgsql_rowlv_get_mode(); - s->prevXactReadOnly = XactReadOnly; - - CurrentTransactionState = s; -diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c -index bb06a2e..77feebf 100644 ---- a/src/backend/access/transam/xlog.c -+++ b/src/backend/access/transam/xlog.c -@@ -8916,7 +8916,7 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS) - * Construct a tuple descriptor for the result row. This must match this - * function's pg_proc entry! - */ -- resultTupleDesc = CreateTemplateTupleDesc(2, false); -+ resultTupleDesc = CreateTemplateTupleDesc(2, false, false); - TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "file_name", - TEXTOID, -1, 0); - TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "file_offset", -diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y -index 9cc6850..7537984 100644 ---- a/src/backend/bootstrap/bootparse.y -+++ b/src/backend/bootstrap/bootparse.y -@@ -32,6 +32,7 @@ - #include "catalog/pg_authid.h" - #include "catalog/pg_class.h" - #include "catalog/pg_namespace.h" -+#include "catalog/pg_seclabel.h" - #include "catalog/pg_tablespace.h" - #include "catalog/toasting.h" - #include "commands/defrem.h" -@@ -187,10 +188,11 @@ Boot_CreateStmt: - TupleDesc tupdesc; - bool shared_relation; - bool mapped_relation; -+ bool hassecid = seclabelCatalogHasSysAttr($3); - - do_start(); - -- tupdesc = CreateTupleDesc(numattr, !($6), attrtypes); -+ tupdesc = CreateTupleDesc(numattr, !($6), hassecid, attrtypes); - - shared_relation = $5; - -@@ -245,7 +247,8 @@ Boot_CreateStmt: - ONCOMMIT_NOOP, - (Datum) 0, - false, -- true); -+ true, -+ NULL); - elog(DEBUG4, "relation created with oid %u", id); - } - do_end(); -diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c -index 080d80e..a870b31 100644 ---- a/src/backend/bootstrap/bootstrap.c -+++ b/src/backend/bootstrap/bootstrap.c -@@ -33,6 +33,7 @@ - #include "postmaster/bgwriter.h" - #include "postmaster/walwriter.h" - #include "replication/walreceiver.h" -+#include "sepgsql/hooks.h" - #include "storage/bufmgr.h" - #include "storage/ipc.h" - #include "storage/proc.h" -@@ -319,6 +320,9 @@ AuxiliaryProcessMain(int argc, char *argv[]) - case WalReceiverProcess: - statmsg = "wal receiver process"; - break; -+ case SecurityWorkerProcess: -+ statmsg = "security worker process"; -+ break; - default: - statmsg = "??? process"; - break; -@@ -428,6 +432,10 @@ AuxiliaryProcessMain(int argc, char *argv[]) - WalReceiverMain(); - proc_exit(1); /* should never return */ - -+ case SecurityWorkerProcess: -+ sepgsql_worker_main(); -+ proc_exit(1); /* should never return */ -+ - default: - elog(PANIC, "unrecognized process type: %d", auxType); - proc_exit(1); -@@ -482,6 +490,11 @@ BootstrapModeMain(void) - boot_yyparse(); - - /* -+ * Initial security labeling -+ */ -+ sepgsql_post_bootstraping(); -+ -+ /* - * We should now know about all mapped relations, so it's okay to write - * out the initial relation mapping files. - */ -@@ -779,6 +792,7 @@ InsertOneTuple(Oid objectid) - - tupDesc = CreateTupleDesc(numattr, - RelationGetForm(boot_reldesc)->relhasoids, -+ RelationGetForm(boot_reldesc)->relhassecids, - attrtypes); - tuple = heap_form_tuple(tupDesc, values, Nulls); - if (objectid != (Oid) 0) -diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile -index a970039..0381d6b 100644 ---- a/src/backend/catalog/Makefile -+++ b/src/backend/catalog/Makefile -@@ -13,7 +13,7 @@ include $(top_builddir)/src/Makefile.global - OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ - pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ - pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ -- pg_db_role_setting.o pg_shdepend.o pg_type.o storage.o toasting.o -+ pg_db_role_setting.o pg_seclabel.o pg_shdepend.o pg_type.o storage.o toasting.o - - BKIFILES = postgres.bki postgres.description postgres.shdescription - -@@ -34,7 +34,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ - pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ - pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \ - pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ -- pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ -+ pg_seclabel.h pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ - pg_ts_parser.h pg_ts_template.h \ - pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ - pg_default_acl.h \ -diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c -index 9b5602e..80be522 100644 ---- a/src/backend/catalog/aclchk.c -+++ b/src/backend/catalog/aclchk.c -@@ -38,6 +38,7 @@ - #include "catalog/pg_operator.h" - #include "catalog/pg_opfamily.h" - #include "catalog/pg_proc.h" -+#include "catalog/pg_seclabel.h" - #include "catalog/pg_tablespace.h" - #include "catalog/pg_type.h" - #include "catalog/pg_ts_config.h" -@@ -46,6 +47,7 @@ - #include "foreign/foreign.h" - #include "miscadmin.h" - #include "parser/parse_func.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -1506,6 +1508,10 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, - if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) - continue; - -+ /* Skip security label column, if it doesn't exist */ -+ if (curr_att == SecurityLabelAttributeNumber && !classForm->relhassecids) -+ continue; -+ - /* Views don't have any system columns at all */ - if (classForm->relkind == RELKIND_VIEW && curr_att < 0) - continue; -@@ -1615,6 +1621,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. -@@ -1876,6 +1884,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. -@@ -2070,6 +2080,8 @@ ExecGrant_Database(InternalGrant *istmt) - datId, grantorId, ACL_KIND_DATABASE, - NameStr(pg_database_tuple->datname), - 0, NULL); -+ /* SELinux checks */ -+ sepgsql_database_grant(datId); - - /* - * Generate new ACL. -@@ -2195,6 +2207,8 @@ ExecGrant_Fdw(InternalGrant *istmt) - fdwid, grantorId, ACL_KIND_FDW, - NameStr(pg_fdw_tuple->fdwname), - 0, NULL); -+ /* SELinux checks */ -+ sepgsql_fdw_grant(fdwid); - - /* - * Generate new ACL. -@@ -2320,6 +2334,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) - srvid, grantorId, ACL_KIND_FOREIGN_SERVER, - NameStr(pg_server_tuple->srvname), - 0, NULL); -+ /* SELinux checks */ -+ sepgsql_fserver_grant(srvid); - - /* - * Generate new ACL. -@@ -2838,6 +2854,8 @@ ExecGrant_Namespace(InternalGrant *istmt) - nspid, grantorId, ACL_KIND_NAMESPACE, - NameStr(pg_namespace_tuple->nspname), - 0, NULL); -+ /* SELinux checks */ -+ sepgsql_schema_grant(nspid); - - /* - * Generate new ACL. -@@ -2962,6 +2980,8 @@ ExecGrant_Tablespace(InternalGrant *istmt) - tblId, grantorId, ACL_KIND_TABLESPACE, - NameStr(pg_tablespace_tuple->spcname), - 0, NULL); -+ /* SELinux checks */ -+ sepgsql_tablespace_grant(tblId); - - /* - * Generate new ACL. -diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c -index 1739085..36d4852 100644 ---- a/src/backend/catalog/catalog.c -+++ b/src/backend/catalog/catalog.c -@@ -32,6 +32,7 @@ - #include "catalog/pg_namespace.h" - #include "catalog/pg_pltemplate.h" - #include "catalog/pg_db_role_setting.h" -+#include "catalog/pg_seclabel.h" - #include "catalog/pg_shdepend.h" - #include "catalog/pg_shdescription.h" - #include "catalog/pg_tablespace.h" -@@ -306,6 +307,7 @@ IsSharedRelation(Oid relationId) - relationId == AuthMemRelationId || - relationId == DatabaseRelationId || - relationId == PLTemplateRelationId || -+ relationId == SecLabelRelationId || - relationId == SharedDescriptionRelationId || - relationId == SharedDependRelationId || - relationId == TableSpaceRelationId || -@@ -319,6 +321,8 @@ IsSharedRelation(Oid relationId) - relationId == DatabaseNameIndexId || - relationId == DatabaseOidIndexId || - relationId == PLTemplateNameIndexId || -+ relationId == SecLabelSecidIndexId || -+ relationId == SecLabelLabelIndexId || - relationId == SharedDescriptionObjIndexId || - relationId == SharedDependDependerIndexId || - relationId == SharedDependReferenceIndexId || -@@ -329,6 +333,8 @@ IsSharedRelation(Oid relationId) - /* These are their toast tables and toast indexes (see toasting.h) */ - if (relationId == PgDatabaseToastTable || - relationId == PgDatabaseToastIndex || -+ relationId == PgSecLabelToastTable || -+ relationId == PgSecLabelToastIndex || - relationId == PgShdescriptionToastTable || - relationId == PgShdescriptionToastIndex || - relationId == PgDbRoleSettingToastTable || -diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl -index 85d1d71..853074e 100644 ---- a/src/backend/catalog/genbki.pl -+++ b/src/backend/catalog/genbki.pl -@@ -218,7 +218,8 @@ foreach my $catname ( @{ $catalogs->{names} } ) - {cmin => 'cid'}, - {xmax => 'xid'}, - {cmax => 'cid'}, -- {tableoid => 'oid'} -+ {tableoid => 'oid'}, -+ {security_label => 'text'} - ); - foreach my $attr (@SYS_ATTRS) - { -diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c -index 47e0c9b..73cbfcf 100644 ---- a/src/backend/catalog/heap.c -+++ b/src/backend/catalog/heap.c -@@ -43,6 +43,7 @@ - #include "catalog/pg_constraint.h" - #include "catalog/pg_inherits.h" - #include "catalog/pg_namespace.h" -+#include "catalog/pg_seclabel.h" - #include "catalog/pg_statistic.h" - #include "catalog/pg_tablespace.h" - #include "catalog/pg_type.h" -@@ -82,14 +83,16 @@ static void AddNewRelationTuple(Relation pg_class_desc, - Oid relowner, - char relkind, - Datum relacl, -- Datum reloptions); -+ Datum reloptions, -+ Oid *secLabels); - static Oid AddNewRelationType(const char *typeName, - Oid typeNamespace, - Oid new_rel_oid, - char new_rel_kind, - Oid ownerid, - Oid new_row_type, -- Oid new_array_type); -+ Oid new_array_type, -+ Oid securityId); - static void RelationRemoveInheritance(Oid relid); - static void StoreRelCheck(Relation rel, char *ccname, Node *expr, - bool is_local, int inhcount); -@@ -173,7 +176,16 @@ static FormData_pg_attribute a7 = { - true, 'p', 'i', true, false, false, true, 0 - }; - --static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; -+/* -+ * Security Label system column -+ */ -+static FormData_pg_attribute a8 = { -+ 0, {"security_label"}, TEXTOID, 0, -1, -+ SecurityLabelAttributeNumber, 0, -1, -1, -+ false, 'x', 'i', true, false, false, true, 0 -+}; -+ -+static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8}; - - /* - * This function returns a Form_pg_attribute pointer for a system attribute. -@@ -181,12 +193,14 @@ static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; - * happen if there's a problem upstream. - */ - Form_pg_attribute --SystemAttributeDefinition(AttrNumber attno, bool relhasoids) -+SystemAttributeDefinition(AttrNumber attno, bool relhasoids, bool relhassecids) - { - if (attno >= 0 || attno < -(int) lengthof(SysAtt)) - elog(ERROR, "invalid system attribute number %d", attno); - if (attno == ObjectIdAttributeNumber && !relhasoids) - elog(ERROR, "invalid system attribute number %d", attno); -+ if (attno == SecurityLabelAttributeNumber && !relhassecids) -+ elog(ERROR, "invalid system attribute number %d", attno); - return SysAtt[-attno - 1]; - } - -@@ -195,7 +209,7 @@ SystemAttributeDefinition(AttrNumber attno, bool relhasoids) - * pointer for a prototype definition. If not, return NULL. - */ - Form_pg_attribute --SystemAttributeByName(const char *attname, bool relhasoids) -+SystemAttributeByName(const char *attname, bool relhasoids, bool relhassecids) - { - int j; - -@@ -203,16 +217,29 @@ SystemAttributeByName(const char *attname, bool relhasoids) - { - Form_pg_attribute att = SysAtt[j]; - -- if (relhasoids || att->attnum != ObjectIdAttributeNumber) -- { -- if (strcmp(NameStr(att->attname), attname) == 0) -- return att; -- } -+ if (!relhasoids && att->attnum == ObjectIdAttributeNumber) -+ continue; -+ if (!relhassecids && att->attnum == SecurityLabelAttributeNumber) -+ continue; -+ -+ if (strcmp(NameStr(att->attname), attname) == 0) -+ return att; - } - - return NULL; - } - -+/* -+ * If the given attribute is writable system attribute, it returns true. -+ */ -+bool -+SystemAttributeWritable(AttrNumber attnum, bool hasoids, bool hassecids) -+{ -+ if (hassecids && attnum == SecurityLabelAttributeNumber) -+ return true; -+ -+ return false; -+} - - /* ---------------------------------------------------------------- - * XXX END OF UGLY HARD CODED BADNESS XXX -@@ -391,7 +418,8 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind, - for (i = 0; i < natts; i++) - { - if (SystemAttributeByName(NameStr(tupdesc->attrs[i]->attname), -- tupdesc->tdhasoid) != NULL) -+ tupdesc->tdhasoid, -+ tupdesc->tdhassecid) != NULL) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_COLUMN), - errmsg("column name \"%s\" conflicts with a system column name", -@@ -509,7 +537,8 @@ CheckAttributeType(const char *attname, Oid atttypid, - void - InsertPgAttributeTuple(Relation pg_attribute_rel, - Form_pg_attribute new_attribute, -- CatalogIndexState indstate) -+ CatalogIndexState indstate, -+ Oid securityId) - { - Datum values[Natts_pg_attribute]; - bool nulls[Natts_pg_attribute]; -@@ -542,6 +571,8 @@ InsertPgAttributeTuple(Relation pg_attribute_rel, - nulls[Anum_pg_attribute_attoptions - 1] = true; - - tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, securityId); - - /* finally insert the new tuple, update the indexes, and clean up */ - simple_heap_insert(pg_attribute_rel, tup); -@@ -566,13 +597,15 @@ AddNewAttributeTuples(Oid new_rel_oid, - TupleDesc tupdesc, - char relkind, - bool oidislocal, -- int oidinhcount) -+ int oidinhcount, -+ Oid *secLabels) - { - Form_pg_attribute attr; - int i; - Relation rel; - CatalogIndexState indstate; - int natts = tupdesc->natts; -+ Oid secid; - ObjectAddress myself, - referenced; - -@@ -596,7 +629,10 @@ AddNewAttributeTuples(Oid new_rel_oid, - attr->attstattarget = -1; - attr->attcacheoff = -1; - -- InsertPgAttributeTuple(rel, attr, indstate); -+ secid = (!secLabels ? InvalidOid : -+ secLabels[i - FirstLowInvalidHeapAttributeNumber]); -+ -+ InsertPgAttributeTuple(rel, attr, indstate, secid); - - /* Add dependency info */ - myself.classId = RelationRelationId; -@@ -624,6 +660,11 @@ AddNewAttributeTuples(Oid new_rel_oid, - SysAtt[i]->attnum == ObjectIdAttributeNumber) - continue; - -+ /* skip security label where appropriate */ -+ if (!tupdesc->tdhassecid && -+ SysAtt[i]->attnum == SecurityLabelAttributeNumber) -+ continue; -+ - memcpy(&attStruct, (char *) SysAtt[i], sizeof(FormData_pg_attribute)); - - /* Fill in the correct relation OID in the copied tuple */ -@@ -636,7 +677,10 @@ AddNewAttributeTuples(Oid new_rel_oid, - attStruct.attinhcount = oidinhcount; - } - -- InsertPgAttributeTuple(rel, &attStruct, indstate); -+ secid = (!secLabels ? InvalidOid -+ : secLabels[SysAtt[i]->attnum - FirstLowInvalidHeapAttributeNumber]); -+ -+ InsertPgAttributeTuple(rel, &attStruct, indstate, secid); - } - } - -@@ -666,7 +710,8 @@ InsertPgClassTuple(Relation pg_class_desc, - Relation new_rel_desc, - Oid new_rel_oid, - Datum relacl, -- Datum reloptions) -+ Datum reloptions, -+ Oid securityId) - { - Form_pg_class rd_rel = new_rel_desc->rd_rel; - Datum values[Natts_pg_class]; -@@ -696,6 +741,7 @@ InsertPgClassTuple(Relation pg_class_desc, - values[Anum_pg_class_relnatts - 1] = Int16GetDatum(rd_rel->relnatts); - values[Anum_pg_class_relchecks - 1] = Int16GetDatum(rd_rel->relchecks); - values[Anum_pg_class_relhasoids - 1] = BoolGetDatum(rd_rel->relhasoids); -+ values[Anum_pg_class_relhassecids - 1] = BoolGetDatum(rd_rel->relhassecids); - values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey); - values[Anum_pg_class_relhasexclusion - 1] = BoolGetDatum(rd_rel->relhasexclusion); - values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules); -@@ -719,6 +765,9 @@ InsertPgClassTuple(Relation pg_class_desc, - */ - HeapTupleSetOid(tup, new_rel_oid); - -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, securityId); -+ - /* finally insert the new tuple, update the indexes, and clean up */ - simple_heap_insert(pg_class_desc, tup); - -@@ -743,9 +792,11 @@ AddNewRelationTuple(Relation pg_class_desc, - Oid relowner, - char relkind, - Datum relacl, -- Datum reloptions) -+ Datum reloptions, -+ Oid *secLabels) - { - Form_pg_class new_rel_reltup; -+ Oid secid; - - /* - * first we update some of the information in our uncataloged relation's -@@ -803,9 +854,11 @@ AddNewRelationTuple(Relation pg_class_desc, - - new_rel_desc->rd_att->tdtypeid = new_type_oid; - -+ secid = (!secLabels ? InvalidOid : secLabels[0]); -+ - /* Now build and insert the tuple */ - InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, -- relacl, reloptions); -+ relacl, reloptions, secid); - } - - -@@ -822,7 +875,8 @@ AddNewRelationType(const char *typeName, - char new_rel_kind, - Oid ownerid, - Oid new_row_type, -- Oid new_array_type) -+ Oid new_array_type, -+ Oid securityId) - { - return - TypeCreate(new_row_type, /* optional predetermined OID */ -@@ -854,7 +908,8 @@ AddNewRelationType(const char *typeName, - 'x', /* fully TOASTable */ - -1, /* typmod */ - 0, /* array dimensions for typBaseType */ -- false); /* Type NOT NULL */ -+ false, /* Type NOT NULL */ -+ securityId); /* security-id of the type */ - } - - /* -------------------------------- -@@ -903,7 +958,8 @@ heap_create_with_catalog(const char *relname, - OnCommitAction oncommit, - Datum reloptions, - bool use_user_acl, -- bool allow_system_table_mods) -+ bool allow_system_table_mods, -+ Oid *secLabels) - { - Relation pg_class_desc; - Relation new_rel_desc; -@@ -911,6 +967,7 @@ heap_create_with_catalog(const char *relname, - Oid old_type_oid; - Oid new_type_oid; - Oid new_array_oid = InvalidOid; -+ Oid type_secid = InvalidOid; - - pg_class_desc = heap_open(RelationRelationId, RowExclusiveLock); - -@@ -1030,6 +1087,11 @@ heap_create_with_catalog(const char *relname, - relkind == RELKIND_COMPOSITE_TYPE)) - new_array_oid = AssignTypeArrayOid(); - -+ /* security context of the relation type */ -+ if (secLabels) -+ type_secid = seclabelMoveSecid(TypeRelationId, -+ RelationRelationId, secLabels[0]); -+ - /* - * Since defining a relation also defines a complex type, we add a new - * system type corresponding to the new relation. The OID of the type can -@@ -1046,7 +1108,8 @@ heap_create_with_catalog(const char *relname, - relkind, - ownerid, - reltypeid, -- new_array_oid); -+ new_array_oid, -+ type_secid); - - /* - * Now make the array type if wanted. -@@ -1086,7 +1149,8 @@ heap_create_with_catalog(const char *relname, - 'x', /* fully TOASTable */ - -1, /* typmod */ - 0, /* array dimensions for typBaseType */ -- false); /* Type NOT NULL */ -+ false, /* Type NOT NULL */ -+ type_secid); /* security-id of the type */ - - pfree(relarrayname); - } -@@ -1106,13 +1170,14 @@ heap_create_with_catalog(const char *relname, - ownerid, - relkind, - PointerGetDatum(relacl), -- reloptions); -+ reloptions, -+ secLabels); - - /* - * now add tuples to pg_attribute for the attributes in our new relation. - */ - AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, -- oidislocal, oidinhcount); -+ oidislocal, oidinhcount, secLabels); - - /* - * Make a dependency link to force the relation to be deleted if its -@@ -1590,6 +1655,11 @@ heap_drop_with_catalog(Oid relid) - * delete relation tuple - */ - DeleteRelationTuple(relid); -+ -+ /* -+ * delete orphan pg_seclabel entries -+ */ -+ seclabelOnDropTable(relid); - } - - -diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c -index 8af382b..01f53fd 100644 ---- a/src/backend/catalog/index.c -+++ b/src/backend/catalog/index.c -@@ -39,6 +39,7 @@ - #include "catalog/pg_constraint.h" - #include "catalog/pg_operator.h" - #include "catalog/pg_opclass.h" -+#include "catalog/pg_seclabel.h" - #include "catalog/pg_tablespace.h" - #include "catalog/pg_trigger.h" - #include "catalog/pg_type.h" -@@ -89,7 +90,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, - Oid *classObjectId); - static void InitializeAttributeOids(Relation indexRelation, - int numatts, Oid indexoid); --static void AppendAttributeTuples(Relation indexRelation, int numatts); -+static void AppendAttributeTuples(Relation indexRelation, -+ int numatts, Oid securityId); - static void UpdateIndexRelation(Oid indexoid, Oid heapoid, - IndexInfo *indexInfo, - Oid *classOids, -@@ -154,7 +156,7 @@ ConstructTupleDescriptor(Relation heapRelation, - /* - * allocate the new tuple descriptor - */ -- indexTupDesc = CreateTemplateTupleDesc(numatts, false); -+ indexTupDesc = CreateTemplateTupleDesc(numatts, false, false); - - /* - * For simple index columns, we copy the pg_attribute row from the parent -@@ -181,7 +183,8 @@ ConstructTupleDescriptor(Relation heapRelation, - * here we are indexing on a system attribute (-1...-n) - */ - from = SystemAttributeDefinition(atnum, -- heapRelation->rd_rel->relhasoids); -+ heapRelation->rd_rel->relhasoids, -+ heapRelation->rd_rel->relhassecids); - } - else - { -@@ -338,13 +341,16 @@ InitializeAttributeOids(Relation indexRelation, - * ---------------------------------------------------------------- - */ - static void --AppendAttributeTuples(Relation indexRelation, int numatts) -+AppendAttributeTuples(Relation indexRelation, int numatts, Oid securityId) - { - Relation pg_attribute; - CatalogIndexState indstate; - TupleDesc indexTupDesc; - int i; - -+ /* copy security id */ -+ securityId = seclabelMoveSecid(AttributeRelationId, -+ RelationRelationId, securityId); - /* - * open the attribute relation and its indexes - */ -@@ -366,7 +372,8 @@ AppendAttributeTuples(Relation indexRelation, int numatts) - Assert(indexTupDesc->attrs[i]->attnum == i + 1); - Assert(indexTupDesc->attrs[i]->attcacheoff == -1); - -- InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate); -+ InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], -+ indstate, securityId); - } - - CatalogCloseIndexes(indstate); -@@ -544,6 +551,7 @@ index_create(Oid heapRelationId, - bool mapped_relation; - bool is_exclusion; - Oid namespaceId; -+ Oid securityId; - int i; - - is_exclusion = (indexInfo->ii_ExclusionOps != NULL); -@@ -681,15 +689,21 @@ index_create(Oid heapRelationId, - indexRelation->rd_rel->relam = accessMethodObjectId; - indexRelation->rd_rel->relkind = RELKIND_INDEX; - indexRelation->rd_rel->relhasoids = false; -+ indexRelation->rd_rel->relhassecids = false; - indexRelation->rd_rel->relhasexclusion = is_exclusion; - - /* -+ * Index always has same security id of the relation to be indexed on. -+ */ -+ securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(heapRelationId)); -+ -+ /* - * store index's pg_class entry - */ - InsertPgClassTuple(pg_class, indexRelation, - RelationGetRelid(indexRelation), - (Datum) 0, -- reloptions); -+ reloptions, securityId); - - /* done with pg_class */ - heap_close(pg_class, RowExclusiveLock); -@@ -705,7 +719,7 @@ index_create(Oid heapRelationId, - /* - * append ATTRIBUTE tuples for the index - */ -- AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs); -+ AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs, securityId); - - /* ---------------- - * update pg_index -diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c -index 2e5bb27..e730904 100644 ---- a/src/backend/catalog/namespace.c -+++ b/src/backend/catalog/namespace.c -@@ -40,6 +40,7 @@ - #include "miscadmin.h" - #include "nodes/makefuncs.h" - #include "parser/parse_func.h" -+#include "sepgsql/hooks.h" - #include "storage/backendid.h" - #include "storage/ipc.h" - #include "utils/acl.h" -@@ -2311,6 +2312,9 @@ LookupExplicitNamespace(const char *nspname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - nspname); - -+ /* SELinux checks */ -+ sepgsql_schema_search(namespaceId, true); -+ - return namespaceId; - } - -@@ -2911,7 +2915,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); - } - } -@@ -2938,7 +2943,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); - } - } -@@ -3004,9 +3010,12 @@ InitTempTableNamespace(void) - char namespaceName[NAMEDATALEN]; - Oid namespaceId; - Oid toastspaceId; -+ Oid secid; - - Assert(!OidIsValid(myTempNamespace)); - -+ snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); -+ - /* - * First, do permission check to see if we are authorized to make temp - * tables. We use a nonstandard error message here since "databasename: -@@ -3024,6 +3033,9 @@ InitTempTableNamespace(void) - errmsg("permission denied to create temporary tables in database \"%s\"", - get_database_name(MyDatabaseId)))); - -+ /* SELinux checks */ -+ secid = sepgsql_schema_create(namespaceName, true); -+ - /* - * Do not allow a Hot Standby slave session to make temp tables. Aside - * from problems with modifying the system catalogs, there is a naming -@@ -3039,8 +3051,6 @@ InitTempTableNamespace(void) - (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), - errmsg("cannot create temporary tables during recovery"))); - -- snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); -- - namespaceId = GetSysCacheOid1(NAMESPACENAME, - CStringGetDatum(namespaceName)); - if (!OidIsValid(namespaceId)) -@@ -3053,7 +3063,9 @@ InitTempTableNamespace(void) - * temp tables. This works because the places that access the temp - * namespace for my own backend skip permissions checks on it. - */ -- namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); -+ namespaceId = NamespaceCreate(namespaceName, -+ BOOTSTRAP_SUPERUSERID, -+ secid); - /* Advance command counter to make namespace visible */ - CommandCounterIncrement(); - } -@@ -3078,7 +3090,9 @@ InitTempTableNamespace(void) - CStringGetDatum(namespaceName)); - if (!OidIsValid(toastspaceId)) - { -- toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); -+ toastspaceId = NamespaceCreate(namespaceName, -+ BOOTSTRAP_SUPERUSERID, -+ secid); - /* Advance command counter to make namespace visible */ - CommandCounterIncrement(); - } -diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c -index 9672ecf..b28a314 100644 ---- a/src/backend/catalog/pg_aggregate.c -+++ b/src/backend/catalog/pg_aggregate.c -@@ -27,6 +27,7 @@ - #include "parser/parse_coerce.h" - #include "parser/parse_func.h" - #include "parser/parse_oper.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -67,6 +68,7 @@ AggregateCreate(const char *aggName, - Oid *fnArgs; - int nargs_transfn; - Oid procOid; -+ Oid procSecid; - TupleDesc tupDesc; - int i; - ObjectAddress myself, -@@ -161,6 +163,10 @@ AggregateCreate(const char *aggName, - } - Assert(OidIsValid(finaltype)); - -+ /* SELinux checks */ -+ procSecid = sepgsql_aggregate_create(aggName, aggNamespace, -+ transfn, finalfn); -+ - /* - * If finaltype (i.e. aggregate return type) is polymorphic, inputs must - * be polymorphic also, else parser will fail to deduce result type. -@@ -229,7 +235,8 @@ AggregateCreate(const char *aggName, - NIL, /* parameterDefaults */ - PointerGetDatum(NULL), /* proconfig */ - 1, /* procost */ -- 0); /* prorows */ -+ 0, /* prorows */ -+ procSecid); /* security-id */ - - /* - * Okay to create the pg_aggregate entry. -diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c -index 574eef5..2a46cdb 100644 ---- a/src/backend/catalog/pg_conversion.c -+++ b/src/backend/catalog/pg_conversion.c -@@ -40,7 +40,7 @@ Oid - ConversionCreate(const char *conname, Oid connamespace, - Oid conowner, - int32 conforencoding, int32 contoencoding, -- Oid conproc, bool def) -+ Oid conproc, bool def, Oid securityId) - { - int i; - Relation rel; -@@ -104,6 +104,8 @@ ConversionCreate(const char *conname, Oid connamespace, - - tup = heap_form_tuple(tupDesc, values, nulls); - -+ HeapTupleSetSecid(tup, securityId); -+ - /* insert a new tuple */ - oid = simple_heap_insert(rel, tup); - Assert(OidIsValid(oid)); -diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c -index 7c5f56f..fb8bf22 100644 ---- a/src/backend/catalog/pg_largeobject.c -+++ b/src/backend/catalog/pg_largeobject.c -@@ -21,10 +21,13 @@ - #include "catalog/dependency.h" - #include "catalog/indexing.h" - #include "catalog/pg_authid.h" -+#include "catalog/pg_description.h" - #include "catalog/pg_largeobject.h" - #include "catalog/pg_largeobject_metadata.h" -+#include "catalog/pg_seclabel.h" - #include "catalog/toasting.h" - #include "miscadmin.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/bytea.h" - #include "utils/fmgroids.h" -@@ -40,7 +43,7 @@ - * will appear to exist with size 0. - */ - Oid --LargeObjectCreate(Oid loid) -+LargeObjectCreate(Oid loid, Oid securityId) - { - Relation pg_lo_meta; - HeapTuple ntup; -@@ -66,6 +69,8 @@ LargeObjectCreate(Oid loid) - if (OidIsValid(loid)) - HeapTupleSetOid(ntup, loid); - -+ HeapTupleSetSecid(ntup, securityId); -+ - loid_new = simple_heap_insert(pg_lo_meta, ntup); - Assert(!OidIsValid(loid) || loid == loid_new); - -@@ -245,6 +250,64 @@ LargeObjectAlterOwner(Oid loid, Oid newOwnerId) - } - - /* -+ * LargeObjectAlterSecLabel -+ * -+ * Implementation of ALTER LARGE OBJECT xxx SECURITY LABEL -+ */ -+void -+LargeObjectAlterSecLabel(Oid loid, char *new_label) -+{ -+ Relation pg_lo_meta; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ Oid securityId; -+ -+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, -+ RowExclusiveLock); -+ -+ ScanKeyInit(&skey, -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loid)); -+ -+ scan = systable_beginscan(pg_lo_meta, -+ LargeObjectMetadataOidIndexId, true, -+ SnapshotNow, 1, &skey); -+ -+ oldtup = systable_getnext(scan); -+ if (!HeapTupleIsValid(oldtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_OBJECT), -+ errmsg("large object %u does not exist", loid))); -+ -+ /* Must be owner of the large object */ -+ if (!pg_largeobject_ownercheck(loid, GetUserId())) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("must be owner of large object %u", loid))); -+ -+ /* SELinux checks */ -+ securityId = sepgsql_largeobject_relabel(loid, new_label); -+ -+ /* update the tuple */ -+ newtup = heap_copytuple(oldtup); -+ -+ HeapTupleSetSecid(newtup, securityId); -+ -+ simple_heap_update(pg_lo_meta, &newtup->t_self, newtup); -+ -+ CatalogUpdateIndexes(pg_lo_meta, newtup); -+ -+ heap_freetuple(newtup); -+ -+ systable_endscan(scan); -+ -+ heap_close(pg_lo_meta, RowExclusiveLock); -+} -+ -+/* - * LargeObjectExists - * - * We don't use the system cache for large object metadata, for fear of -diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c -index 79d03b3..4357a9e 100644 ---- a/src/backend/catalog/pg_namespace.c -+++ b/src/backend/catalog/pg_namespace.c -@@ -28,7 +28,7 @@ - * --------------- - */ - Oid --NamespaceCreate(const char *nspName, Oid ownerId) -+NamespaceCreate(const char *nspName, Oid ownerId, Oid secid) - { - Relation nspdesc; - HeapTuple tup; -@@ -64,6 +64,8 @@ NamespaceCreate(const char *nspName, Oid ownerId) - tupDesc = nspdesc->rd_att; - - tup = heap_form_tuple(tupDesc, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, secid); - - nspoid = simple_heap_insert(nspdesc, tup); - Assert(OidIsValid(nspoid)); -diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c -index 8eebb1d..71be3c5 100644 ---- a/src/backend/catalog/pg_operator.c -+++ b/src/backend/catalog/pg_operator.c -@@ -28,6 +28,7 @@ - #include "catalog/pg_type.h" - #include "miscadmin.h" - #include "parser/parse_oper.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -204,6 +205,7 @@ OperatorShellMake(const char *operatorName, - { - Relation pg_operator_desc; - Oid operatorObjectId; -+ Oid securityId; - int i; - HeapTuple tup; - Datum values[Natts_pg_operator]; -@@ -220,6 +222,12 @@ OperatorShellMake(const char *operatorName, - errmsg("\"%s\" is not a valid operator name", - operatorName))); - -+ /* SELinux checks */ -+ securityId = sepgsql_operator_create(operatorName, InvalidOid, -+ operatorNamespace, -+ InvalidOid, InvalidOid, InvalidOid, -+ InvalidOid, InvalidOid); -+ - /* - * initialize our *nulls and *values arrays - */ -@@ -261,6 +269,8 @@ OperatorShellMake(const char *operatorName, - */ - tup = heap_form_tuple(tupDesc, values, nulls); - -+ HeapTupleSetSecid(tup, securityId); -+ - /* - * insert our "shell" operator tuple - */ -@@ -340,6 +350,7 @@ OperatorCreate(const char *operatorName, - bool replaces[Natts_pg_operator]; - Datum values[Natts_pg_operator]; - Oid operatorObjectId; -+ Oid securityId; - bool operatorAlreadyDefined; - Oid operResultType; - Oid commutatorId, -@@ -476,6 +487,12 @@ OperatorCreate(const char *operatorName, - else - negatorId = InvalidOid; - -+ /* SELinux checks */ -+ securityId = sepgsql_operator_create(operatorName, operatorObjectId, -+ operatorNamespace, -+ procedureId, restrictionId, joinId, -+ commutatorId, negatorId); -+ - /* - * set up values in the operator tuple - */ -@@ -516,6 +533,8 @@ OperatorCreate(const char *operatorName, - if (!HeapTupleIsValid(tup)) - elog(ERROR, "cache lookup failed for operator %u", - operatorObjectId); -+ if (securityId != HeapTupleHasSecid(tup)) -+ elog(ERROR, "Bug? security-id was mismatched."); - - tup = heap_modify_tuple(tup, - RelationGetDescr(pg_operator_desc), -@@ -530,6 +549,8 @@ OperatorCreate(const char *operatorName, - tupDesc = pg_operator_desc->rd_att; - tup = heap_form_tuple(tupDesc, values, nulls); - -+ HeapTupleSetSecid(tup, securityId); -+ - operatorObjectId = simple_heap_insert(pg_operator_desc, tup); - } - -diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c -index f60cc61..a2ccc76 100644 ---- a/src/backend/catalog/pg_proc.c -+++ b/src/backend/catalog/pg_proc.c -@@ -84,7 +84,8 @@ ProcedureCreate(const char *procedureName, - List *parameterDefaults, - Datum proconfig, - float4 procost, -- float4 prorows) -+ float4 prorows, -+ Oid prosecid) - { - Oid retval; - int parameterCount; -@@ -364,6 +365,8 @@ ProcedureCreate(const char *procedureName, - if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), proowner)) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - procedureName); -+ if (prosecid != HeapTupleGetSecid(oldtup)) -+ elog(ERROR, "Bug? security-id was tried to be changed."); - - /* - * Not okay to change the return type of the existing proc, since -@@ -548,6 +551,9 @@ ProcedureCreate(const char *procedureName, - nulls[Anum_pg_proc_proacl - 1] = true; - - tup = heap_form_tuple(tupDesc, values, nulls); -+ -+ HeapTupleSetSecid(tup, prosecid); -+ - simple_heap_insert(rel, tup); - is_update = false; - } -diff --git a/src/backend/catalog/pg_seclabel.c b/src/backend/catalog/pg_seclabel.c -new file mode 100644 -index 0000000..4816635 ---- /dev/null -+++ b/src/backend/catalog/pg_seclabel.c -@@ -0,0 +1,646 @@ -+/* -+ * pg_seclabel.c -+ * -+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#include "postgres.h" -+ -+#include "access/genam.h" -+#include "access/heapam.h" -+#include "access/sysattr.h" -+#include "access/tupdesc.h" -+#include "catalog/catalog.h" -+#include "catalog/indexing.h" -+#include "catalog/pg_aggregate.h" -+#include "catalog/pg_amop.h" -+#include "catalog/pg_amproc.h" -+#include "catalog/pg_attrdef.h" -+#include "catalog/pg_auth_members.h" -+#include "catalog/pg_constraint.h" -+#include "catalog/pg_db_role_setting.h" -+#include "catalog/pg_depend.h" -+#include "catalog/pg_description.h" -+#include "catalog/pg_enum.h" -+#include "catalog/pg_index.h" -+#include "catalog/pg_inherits.h" -+#include "catalog/pg_largeobject.h" -+#include "catalog/pg_namespace.h" -+#include "catalog/pg_rewrite.h" -+#include "catalog/pg_seclabel.h" -+#include "catalog/pg_shdepend.h" -+#include "catalog/pg_shdescription.h" -+#include "catalog/pg_trigger.h" -+#include "catalog/pg_ts_config_map.h" -+#include "executor/spi.h" -+#include "miscadmin.h" -+#include "sepgsql/sepgsql.h" -+#include "sepgsql/hooks.h" -+#include "utils/builtins.h" -+#include "utils/fmgroids.h" -+#include "utils/lsyscache.h" -+#include "utils/rel.h" -+#include "utils/syscache.h" -+#include "utils/tqual.h" -+ -+bool ignore_security_label_input; -+ -+void -+seclabelOnCreateDatabase(Oid src_datid, Oid dst_datid) -+{ -+ Relation rel; -+ ScanKeyData keys[1]; -+ SysScanDesc scan; -+ HeapTuple oldtup, newtup; -+ Datum values[Natts_pg_seclabel]; -+ bool nulls[Natts_pg_seclabel]; -+ bool replaces[Natts_pg_seclabel]; -+ -+ /* Scan all entries with pg_seclabel.datid = src_datid */ -+ ScanKeyInit(&keys[0], -+ Anum_pg_seclabel_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(src_datid)); -+ -+ rel = heap_open(SecLabelRelationId, RowExclusiveLock); -+ -+ scan = systable_beginscan(rel, SecLabelSecidIndexId, true, -+ SnapshotNow, 1, keys); -+ -+ /* corresponding entries will be inserted with new datid */ -+ memset(values, 0, sizeof(values)); -+ memset(nulls, false, sizeof(nulls)); -+ memset(replaces, false, sizeof(replaces)); -+ -+ values[Anum_pg_seclabel_datid - 1] = ObjectIdGetDatum(dst_datid); -+ replaces[Anum_pg_seclabel_datid - 1] = true; -+ while (HeapTupleIsValid(oldtup = systable_getnext(scan))) -+ { -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), -+ values, nulls, replaces); -+ simple_heap_insert(rel, newtup); -+ -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ } -+ systable_endscan(scan); -+ -+ heap_close(rel, RowExclusiveLock); -+} -+ -+void -+seclabelOnDropDatabase(Oid datid) -+{ -+ Relation rel; -+ ScanKeyData keys[1]; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ -+ /* Scan all entries with pg_seclabel.datid = datid */ -+ ScanKeyInit(&keys[0], -+ Anum_pg_seclabel_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(datid)); -+ -+ rel = heap_open(SecLabelRelationId, RowExclusiveLock); -+ -+ scan = systable_beginscan(rel, SecLabelSecidIndexId, true, -+ SnapshotNow, 1, keys); -+ -+ while (HeapTupleIsValid(tuple = systable_getnext(scan))) -+ { -+ simple_heap_delete(rel, &tuple->t_self); -+ } -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, RowExclusiveLock); -+} -+ -+void -+seclabelOnDropTable(Oid relid) -+{ -+ Relation rel; -+ SysScanDesc scan; -+ ScanKeyData key[2]; -+ HeapTuple tuple; -+ Oid database_oid; -+ -+ database_oid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); -+ ScanKeyInit(&key[0], -+ Anum_pg_seclabel_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(database_oid)); -+ ScanKeyInit(&key[1], -+ Anum_pg_seclabel_relid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(relid)); -+ -+ rel = heap_open(SecLabelRelationId, RowExclusiveLock); -+ scan = systable_beginscan(rel, SecLabelLabelIndexId, true, -+ SnapshotNow, 2, key); -+ -+ while (HeapTupleIsValid(tuple = systable_getnext(scan))) -+ simple_heap_delete(rel, &tuple->t_self); -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, RowExclusiveLock); -+} -+ -+/* -+ * XXX - it should be replaced by BKI scripts -+ */ -+bool -+seclabelCatalogHasSysAttr(Oid relOid) -+{ -+ switch (relOid) -+ { -+ case AggregateRelationId: -+ /* pg_aggregate is property of pg_proc */ -+ case AccessMethodOperatorRelationId: -+ /* pg_amop is property of pg_opfamily */ -+ case AccessMethodProcedureRelationId: -+ /* pg_amproc is property of pg_opfamily */ -+ case AttrDefaultRelationId: -+ /* pg_attrdef is property of pg_attribute */ -+ case AuthMemRelationId: -+ /* pg_auth_members is property of pg_auth */ -+ case ConstraintRelationId: -+ /* -+ * CHECK constraint is property of pg_class -+ * DOMAIN constraint is property of pg_type -+ * Global assertion is property of pg_database -+ */ -+ case DbRoleSettingRelationId: -+ /* pg_db_role_setting is property of pg_auth or pg_database */ -+ case DependRelationId: -+ /* property of the depending object */ -+ case DescriptionRelationId: -+ /* property of the object commented on */ -+ case EnumRelationId: -+ /* pg_enum is property of pg_type */ -+ case IndexRelationId: -+ /* pg_index is property of pg_class with RELKIND_INDEX */ -+ case InheritsRelationId: -+ /* pg_inherits is property of the child relation */ -+ case LargeObjectRelationId: -+ /* pg_largeobject is data chunk of pg_largeobject_metadata */ -+ case RewriteRelationId: -+ /* pg_rewrite is property of pg_class */ -+ case SecLabelRelationId: -+ /* No security attribute has no security label */ -+ case SharedDependRelationId: -+ /* property of the depending shared object */ -+ case SharedDescriptionRelationId: -+ /* property of the shared object commented on */ -+ case TriggerRelationId: -+ /* pg_trigger is property of pg_class */ -+ case TSConfigMapRelationId: -+ /* pg_ts_config_map is property of pg_ts_config */ -+ return false; -+ -+ default: -+ return true; -+ } -+} -+ -+Oid * -+seclabelMakeRelationDefaults(TupleDesc tupdesc, List *supOids) -+{ -+ ListCell *l; -+ Oid *secLabels; -+ Oid securityId; -+ int index, attno, nitems; -+ -+ nitems = tupdesc->natts - FirstLowInvalidHeapAttributeNumber; -+ secLabels = palloc0(sizeof(Oid) * nitems); -+ -+ foreach (l, supOids) -+ { -+ Oid relOid = lfirst_oid(l); -+ -+ securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); -+ -+ if (!OidIsValid(secLabels[0])) -+ secLabels[0] = securityId; -+ else if (!seclabelCompareSecid(RelationRelationId, secLabels[0], -+ RelationRelationId, securityId)) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("cannot inherit relations with different label"))); -+ -+ for (index = 1; index < nitems; index++) -+ { -+ attno = index + FirstLowInvalidHeapAttributeNumber; -+ -+ if (attno < 0) -+ securityId = GetSysCacheSecid2(ATTNUM, -+ ObjectIdGetDatum(relOid), -+ Int16GetDatum(attno)); -+ else -+ { -+ const char *attname = NameStr(tupdesc->attrs[attno]->attname); -+ securityId = GetSysCacheSecid2(ATTNAME, -+ ObjectIdGetDatum(relOid), -+ PointerGetDatum(attname)); -+ } -+ -+ if (!OidIsValid(securityId)) -+ continue; -+ -+ if (!OidIsValid(secLabels[index])) -+ secLabels[index] = securityId; -+ else if (!seclabelCompareSecid(AttributeRelationId, secLabels[index], -+ AttributeRelationId, securityId)) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("cannot inherit attribute with different label"))); -+ } -+ } -+ return secLabels; -+} -+ -+Oid * -+seclabelMakeToastDefaults(TupleDesc tupdesc, Oid relOid) -+{ -+ Oid *secLabels; -+ Oid securityId; -+ int index, nitems; -+ -+ nitems = tupdesc->natts + 1 - FirstLowInvalidHeapAttributeNumber; -+ secLabels = palloc0(sizeof(Oid) * nitems); -+ -+ securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); -+ secLabels[0] = securityId; -+ -+ securityId = seclabelMoveSecid(AttributeRelationId, -+ RelationRelationId, -+ securityId); -+ for (index = 1; index < nitems; index++) -+ secLabels[index] = securityId; -+ -+ return secLabels; -+} -+ -+Oid -+seclabelGetNewSecid(Relation rel, HeapTuple tuple) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled() && -+ !IsBootstrapProcessingMode()) -+ { -+ return sepgsql_get_default_secid(rel, tuple); -+ } -+#endif -+ return InvalidOid; -+} -+ -+static Oid -+inputSecurityLabel(Oid relid, const char *seclabel) -+{ -+ LOCKMODE lockmode = AccessShareLock; -+ Relation rel; -+ ScanKeyData skey[3]; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ Oid datid; -+ Oid secid; -+ Datum values[Natts_pg_seclabel]; -+ bool nulls[Natts_pg_seclabel]; -+ -+ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); -+ -+retry: -+ /* -+ * Lookup pg_seclabel first, then insert a new entry if not found. -+ * An exclusive lock is not necessary for the first read-only path, -+ * and we assume most of trials are read-only. -+ */ -+ rel = heap_open(SecLabelRelationId, lockmode); -+ -+ ScanKeyInit(&skey[0], -+ Anum_pg_seclabel_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(datid)); -+ ScanKeyInit(&skey[1], -+ Anum_pg_seclabel_relid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(relid)); -+ ScanKeyInit(&skey[2], -+ Anum_pg_seclabel_label, -+ BTEqualStrategyNumber, F_TEXTEQ, -+ CStringGetTextDatum(seclabel)); -+ -+ scan = systable_beginscan(rel, SecLabelLabelIndexId, true, -+ SnapshotToast, 3, skey); -+ -+ tuple = systable_getnext(scan); -+ if (HeapTupleIsValid(tuple)) -+ { -+ secid = ((Form_pg_seclabel) GETSTRUCT(tuple))->secid; -+ systable_endscan(scan); -+ heap_close(rel, lockmode); -+ return secid; -+ } -+ -+ /* -+ * If not exist, try to insert a new entry. -+ */ -+ if (lockmode == AccessShareLock) -+ { -+ systable_endscan(scan); -+ heap_close(rel, lockmode); -+ lockmode = RowExclusiveLock; -+ goto retry; -+ } -+ -+ memset(nulls, false, sizeof(nulls)); -+ secid = GetNewOidWithIndex(rel, SecLabelSecidIndexId, -+ Anum_pg_seclabel_secid); -+ values[Anum_pg_seclabel_secid - 1] = ObjectIdGetDatum(secid); -+ values[Anum_pg_seclabel_datid - 1] = ObjectIdGetDatum(datid); -+ values[Anum_pg_seclabel_relid - 1] = ObjectIdGetDatum(relid); -+ values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(seclabel); -+ -+ tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); -+ -+ simple_heap_insert(rel, tuple); -+ -+ CatalogUpdateIndexes(rel, tuple); -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, lockmode); -+ -+ return secid; -+} -+ -+static char * -+outputSecurityLabel(Oid relid, Oid secid) -+{ -+ Relation rel; -+ ScanKeyData skey[3]; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ Oid datid; -+ char *result = NULL; -+ -+ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); -+ -+ /* -+ * Lookup pg_seclabel for the given datid/relid/secid -+ */ -+ rel = heap_open(SecLabelRelationId, AccessShareLock); -+ -+ ScanKeyInit(&skey[0], -+ Anum_pg_seclabel_secid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(secid)); -+ ScanKeyInit(&skey[1], -+ Anum_pg_seclabel_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(datid)); -+ ScanKeyInit(&skey[2], -+ Anum_pg_seclabel_relid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(relid)); -+ -+ scan = systable_beginscan(rel, SecLabelSecidIndexId, true, -+ SnapshotToast, 3, skey); -+ tuple = systable_getnext(scan); -+ if (HeapTupleIsValid(tuple)) -+ { -+ Datum datum; -+ bool isnull; -+ -+ datum = heap_getattr(tuple, -+ Anum_pg_seclabel_label, -+ RelationGetDescr(rel), &isnull); -+ if (!isnull) -+ result = TextDatumGetCString(datum); -+ } -+ systable_endscan(scan); -+ -+ heap_close(rel, AccessShareLock); -+ -+ return result; -+} -+ -+Oid -+seclabelRawInput(Oid relid, char *seclabel) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ seclabel = sepgsql_rawlabel_in(seclabel); -+#endif -+ return inputSecurityLabel(relid, seclabel); -+} -+ -+char * -+seclabelRawOutput(Oid relid, Oid secid) -+{ -+ char *seclabel = outputSecurityLabel(relid, secid); -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ seclabel = sepgsql_rawlabel_out(seclabel); -+#endif -+ return seclabel; -+} -+ -+Oid -+seclabelTransInput(Oid relid, char *seclabel) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ seclabel = sepgsql_mcstrans_in(seclabel); -+#endif -+ return seclabelRawInput(relid, seclabel); -+} -+ -+char * -+seclabelTransOutput(Oid relid, Oid secid) -+{ -+ char *seclabel = seclabelRawOutput(relid, secid); -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ seclabel = sepgsql_mcstrans_out(seclabel); -+#endif -+ return seclabel; -+} -+ -+Oid -+seclabelMoveSecid(Oid dst_relid, Oid src_relid, Oid secid) -+{ -+ char *label = seclabelRawOutput(src_relid, secid); -+ -+ if (!label) -+ return InvalidOid; -+ -+ secid = seclabelRawInput(dst_relid, label); -+ -+ pfree(label); -+ -+ return secid; -+} -+ -+bool -+seclabelCompareSecid(Oid relid1, Oid secid1, Oid relid2, Oid secid2) -+{ -+ char *label1 = seclabelRawOutput(relid1, secid1); -+ char *label2 = seclabelRawOutput(relid2, secid2); -+ bool retval = false; -+ -+ if (label1 && label2 && strcmp(label1, label2) == 0) -+ retval = true; -+ else if (!label1 && !label2) -+ retval = false; -+ -+ if (label1) -+ pfree(label1); -+ if (label2) -+ pfree(label2); -+ -+ return retval; -+} -+ -+Datum -+seclabelSysattOutput(Oid relid, HeapTuple tuple) -+{ -+ Oid secid; -+ char *seclabel; -+ -+ secid = HeapTupleGetSecid(tuple); -+ -+ seclabel = seclabelTransOutput(relid, secid); -+ if (!seclabel) -+ seclabel = "unlabeled"; -+ -+ return CStringGetTextDatum(seclabel); -+} -+ -+/* -+ * seclabelRelationReclaim -+ * -+ * It reclaims security labels already referenced to. -+ * It has to be called under the VACUUM FULL context that means the relation -+ * to be reclaimed is already locked exclusively. -+ */ -+void -+seclabelRelationReclaim(Oid relOid) -+{ -+ StringInfoData query; -+ const char *nspname_reclaimed; -+ const char *relname_reclaimed; -+ const char *nspname_pg_seclabel; -+ const char *relname_pg_seclabel; -+ const char *attname_secid; -+ const char *attname_datid; -+ const char *attname_relid; -+ const char *attname_label; -+ const char *nspname_to_secid; -+ const char *proname_to_secid; -+ Oid databaseId; -+ Oid namespaceId; -+ int index; -+ int save_sepgsql_mode; -+ -+ if (SPI_connect() != SPI_OK_CONNECT) -+ elog(ERROR, "SPI_connect() failed"); -+ -+ /* -+ * DELETE orphan entries -+ */ -+ databaseId = (IsSharedRelation(relOid) ? InvalidOid : MyDatabaseId); -+ -+ namespaceId = get_rel_namespace(relOid); -+ nspname_reclaimed = get_namespace_name(namespaceId); -+ relname_reclaimed = get_rel_name(relOid); -+ -+ namespaceId = get_rel_namespace(SecLabelRelationId); -+ nspname_pg_seclabel = get_namespace_name(namespaceId); -+ relname_pg_seclabel = get_rel_name(SecLabelRelationId); -+ -+ attname_secid = get_attname(SecLabelRelationId, Anum_pg_seclabel_secid); -+ attname_datid = get_attname(SecLabelRelationId, Anum_pg_seclabel_datid); -+ attname_relid = get_attname(SecLabelRelationId, Anum_pg_seclabel_relid); -+ attname_label = get_attname(SecLabelRelationId, Anum_pg_seclabel_label); -+ -+ namespaceId = get_func_namespace(F_SECLABEL_TO_SECID); -+ nspname_to_secid = get_namespace_name(namespaceId); -+ proname_to_secid = get_func_name(F_SECLABEL_TO_SECID); -+ -+ initStringInfo(&query); -+ appendStringInfo(&query, -+ "DELETE FROM %s.%s " -+ "WHERE %s = %u AND %s = %u AND %s NOT IN " -+ "(SELECT %s.%s(%s) FROM ONLY %s.%s) " -+ "RETURNING %s,%s", -+ quote_identifier(nspname_pg_seclabel), -+ quote_identifier(relname_pg_seclabel), -+ quote_identifier(attname_datid), -+ databaseId, -+ quote_identifier(attname_relid), -+ relOid, -+ quote_identifier(attname_secid), -+ quote_identifier(nspname_to_secid), -+ quote_identifier(proname_to_secid), -+ quote_identifier(relname_reclaimed), -+ quote_identifier(nspname_reclaimed), -+ quote_identifier(relname_reclaimed), -+ quote_identifier(attname_secid), -+ quote_identifier(attname_label)); -+ /* -+ * Run the query -+ */ -+ elog(DEBUG1, "query: %s", query.data); -+ -+ save_sepgsql_mode = sepostgresql_mode; -+ -+ PG_TRY(); -+ { -+ sepostgresql_mode = SEPGSQL_MODE_INTERNAL; -+ -+ if (SPI_execute(query.data, false, 0) != SPI_OK_DELETE_RETURNING) -+ elog(ERROR, "Failed to run: %s", query.data); -+ } -+ PG_CATCH(); -+ { -+ sepostgresql_mode = save_sepgsql_mode; -+ -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ -+ sepostgresql_mode = save_sepgsql_mode; -+ -+ for (index = 0; index < SPI_processed; index++) -+ { -+ HeapTuple tuple = SPI_tuptable->vals[index]; -+ char *label; -+ Oid secid; -+ Datum datum; -+ bool isnull; -+ -+ datum = heap_getattr(tuple, 1, SPI_tuptable->tupdesc, &isnull); -+ secid = (isnull ? InvalidOid : DatumGetObjectId(datum)); -+ -+ datum = heap_getattr(tuple, 2, SPI_tuptable->tupdesc, &isnull); -+ label = (isnull ? NULL : TextDatumGetCString(datum)); -+ -+ elog(DEBUG1, "seclabel: \"%s\"was reclaimed (secid=%u)", -+ label, secid); -+ } -+ -+ if (SPI_finish() != SPI_OK_FINISH) -+ elog(ERROR, "SPI_finish() failed"); -+} -+ -+Datum -+seclabel_to_secid(PG_FUNCTION_ARGS) -+{ -+ HeapTupleHeader htup = PG_GETARG_HEAPTUPLEHEADER(0); -+ -+ PG_RETURN_OID(HeapTupleHeaderGetSecid(htup)); -+} -diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c -index d4fdea9..a1220ea 100644 ---- a/src/backend/catalog/pg_type.c -+++ b/src/backend/catalog/pg_type.c -@@ -25,6 +25,7 @@ - #include "commands/typecmds.h" - #include "miscadmin.h" - #include "parser/scansup.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -57,10 +58,17 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) - Datum values[Natts_pg_type]; - bool nulls[Natts_pg_type]; - Oid typoid; -+ Oid secid; - NameData name; - - Assert(PointerIsValid(typeName)); - -+ /* SELinux checks */ -+ secid = sepgsql_type_create(typeName, InvalidOid, -+ typeNamespace, TYPTYPE_PSEUDO, -+ F_SHELL_IN, F_SHELL_OUT, -+ InvalidOid, InvalidOid, -+ InvalidOid, InvalidOid, InvalidOid); - /* - * open pg_type - */ -@@ -126,6 +134,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) - binary_upgrade_next_pg_type_oid = InvalidOid; - } - -+ HeapTupleSetSecid(tup, secid); -+ - /* - * insert the tuple in the relation and get the tuple's oid. - */ -@@ -204,7 +214,8 @@ TypeCreate(Oid newTypeOid, - char storage, - int32 typeMod, - int32 typNDims, /* Array dimensions for baseType */ -- bool typeNotNull) -+ bool typeNotNull, -+ Oid securityId) - { - Relation pg_type_desc; - Oid typeObjectId; -@@ -389,6 +400,8 @@ TypeCreate(Oid newTypeOid, - */ - if (((Form_pg_type) GETSTRUCT(tup))->typowner != ownerId) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, typeName); -+ if (HeapTupleGetSecid(tup) != securityId) -+ elog(ERROR, "Bug? security-id was mismatched"); - - /* trouble if caller wanted to force the OID */ - if (OidIsValid(newTypeOid)) -@@ -425,6 +438,8 @@ TypeCreate(Oid newTypeOid, - } - /* else allow system to assign oid */ - -+ HeapTupleSetSecid(tup, securityId); -+ - typeObjectId = simple_heap_insert(pg_type_desc, tup); - } - -diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c -index 86e7daa..82cf587 100644 ---- a/src/backend/catalog/toasting.c -+++ b/src/backend/catalog/toasting.c -@@ -24,6 +24,7 @@ - #include "catalog/namespace.h" - #include "catalog/pg_namespace.h" - #include "catalog/pg_opclass.h" -+#include "catalog/pg_seclabel.h" - #include "catalog/pg_type.h" - #include "catalog/toasting.h" - #include "miscadmin.h" -@@ -127,6 +128,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio - int16 coloptions[2]; - ObjectAddress baseobject, - toastobject; -+ Oid *secLabels; - - /* - * Toast table is shared if and only if its parent is. -@@ -168,7 +170,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio - "pg_toast_%u_index", relOid); - - /* this is pretty painful... need a tuple descriptor */ -- tupdesc = CreateTemplateTupleDesc(3, false); -+ tupdesc = CreateTemplateTupleDesc(3, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, - "chunk_id", - OIDOID, -@@ -206,6 +208,11 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio - binary_upgrade_next_pg_type_toast_oid = InvalidOid; - } - -+ /* -+ * Toast inherits security-id from the heap relation -+ */ -+ secLabels = seclabelMakeToastDefaults(tupdesc, relOid); -+ - toast_relid = heap_create_with_catalog(toast_relname, - namespaceid, - rel->rd_rel->reltablespace, -@@ -223,7 +230,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio - ONCOMMIT_NOOP, - reloptions, - false, -- true); -+ true, -+ secLabels); - - /* make the toast relation visible, else index creation will fail */ - CommandCounterIncrement(); -diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c -index 2c6ce59..312d59f 100644 ---- a/src/backend/commands/aggregatecmds.c -+++ b/src/backend/commands/aggregatecmds.c -@@ -32,6 +32,7 @@ - #include "miscadmin.h" - #include "parser/parse_func.h" - #include "parser/parse_type.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -307,6 +308,9 @@ RenameAggregate(List *name, List *args, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux checks */ -+ sepgsql_proc_alter_rename(procOid, newname); -+ - /* rename */ - namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname); - simple_heap_update(rel, &tup->t_self, tup); -diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c -index cecdfa5..5b51f68 100644 ---- a/src/backend/commands/alter.c -+++ b/src/backend/commands/alter.c -@@ -290,3 +290,64 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) - (int) stmt->objectType); - } - } -+ -+/* -+ * ExecAlterSecLabelStmt -+ * -+ * Execute ALTER xxx SECURITY LABEL TO statement -+ */ -+void -+ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt) -+{ -+ const char *name; -+ char *seclabel; -+ -+ Assert(IsA(stmt->secLabel, String)); -+ seclabel = strVal(stmt->secLabel); -+ -+ switch (stmt->objectType) -+ { -+ case OBJECT_DATABASE: -+ name = strVal(linitial(stmt->object)); -+ AlterDatabaseSecLabel(name, seclabel); -+ break; -+ -+ case OBJECT_SCHEMA: -+ name = strVal(linitial(stmt->object)); -+ AlterSchemaSecLabel(name, seclabel); -+ break; -+ -+ case OBJECT_TABLE: -+ case OBJECT_SEQUENCE: -+ case OBJECT_VIEW: -+ case OBJECT_COLUMN: -+ AlterRelationSecLabel(stmt->relation, stmt->addname, -+ stmt->objectType, seclabel); -+ break; -+ -+ case OBJECT_AGGREGATE: -+ AlterFunctionSecLabel(stmt->object, stmt->objarg, true, seclabel); -+ break; -+ -+ case OBJECT_FUNCTION: -+ AlterFunctionSecLabel(stmt->object, stmt->objarg, false, seclabel); -+ break; -+ -+ case OBJECT_LARGEOBJECT: -+ LargeObjectAlterSecLabel(intVal(linitial(stmt->object)), seclabel); -+ break; -+ -+ case OBJECT_TYPE: -+ case OBJECT_DOMAIN: -+ AlterTypeSecLabel(stmt->object, seclabel); -+ break; -+ -+ case OBJECT_TABLESPACE: -+ AlterTableSpaceSecLabel(strVal(linitial(stmt->object)), seclabel); -+ break; -+ -+ default: -+ elog(ERROR, "unrecognized AlterSecLabelStmt type: %d", -+ (int) stmt->objectType); -+ } -+} -diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c -index 61020dc..f8408b4 100644 ---- a/src/backend/commands/cluster.c -+++ b/src/backend/commands/cluster.c -@@ -30,12 +30,14 @@ - #include "catalog/indexing.h" - #include "catalog/namespace.h" - #include "catalog/pg_namespace.h" -+#include "catalog/pg_seclabel.h" - #include "catalog/toasting.h" - #include "commands/cluster.h" - #include "commands/tablecmds.h" - #include "commands/trigger.h" - #include "commands/vacuum.h" - #include "miscadmin.h" -+#include "sepgsql/hooks.h" - #include "storage/bufmgr.h" - #include "storage/procarray.h" - #include "storage/smgr.h" -@@ -116,6 +118,9 @@ cluster(ClusterStmt *stmt, bool isTopLevel) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(rel)); - -+ /* SELinux checks */ -+ sepgsql_relation_cluster(tableOid, true); -+ - /* - * Reject clustering a remote temp table ... their local buffer - * manager is not going to cope. -@@ -290,7 +295,8 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose, - Form_pg_index indexForm; - - /* Check that the user still owns the relation */ -- if (!pg_class_ownercheck(tableOid, GetUserId())) -+ if (!pg_class_ownercheck(tableOid, GetUserId()) || -+ !sepgsql_relation_cluster(tableOid, false)) - { - relation_close(OldHeap, AccessExclusiveLock); - return; -@@ -632,6 +638,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) - HeapTuple tuple; - Datum reloptions; - bool isNull; -+ Oid *secLabels; - - OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock); - OldHeapDesc = RelationGetDescr(OldHeap); -@@ -657,6 +664,11 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) - reloptions = (Datum) 0; - - /* -+ * The new heap copies all the security-id from the original -+ */ -+ secLabels = seclabelMakeRelationDefaults(tupdesc, -+ list_make1_oid(OIDOldHeap)); -+ /* - * Create the new heap, using a temporary name in the same namespace as - * the existing table. NOTE: there is some risk of collision with user - * relnames. Working around this seems more trouble than it's worth; in -@@ -687,7 +699,8 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) - ONCOMMIT_NOOP, - reloptions, - false, -- true); -+ true, -+ secLabels); - - ReleaseSysCache(tuple); - -@@ -977,6 +990,9 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, - /* Preserve OID, if any */ - if (NewHeap->rd_rel->relhasoids) - HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); -+ /* Preserve security-id, if any */ -+ if (NewHeap->rd_rel->relhassecids) -+ HeapTupleSetSecid(copiedTuple, HeapTupleGetSecid(tuple)); - - /* The heap rewrite module does the rest */ - rewrite_heap_tuple(rwstate, tuple, copiedTuple); -@@ -1469,7 +1485,8 @@ get_tables_to_cluster(MemoryContext cluster_context) - { - index = (Form_pg_index) GETSTRUCT(indexTuple); - -- if (!pg_class_ownercheck(index->indrelid, GetUserId())) -+ if (!pg_class_ownercheck(index->indrelid, GetUserId()) || -+ !sepgsql_relation_cluster(index->indrelid, false)) - continue; - - /* -diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c -index 7fa09c8..2deb171 100644 ---- a/src/backend/commands/comment.c -+++ b/src/backend/commands/comment.c -@@ -49,6 +49,7 @@ - #include "parser/parse_func.h" - #include "parser/parse_oper.h" - #include "parser/parse_type.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -548,6 +549,9 @@ CommentRelation(int objtype, List *relname, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(relation)); - -+ /* SELinux checks */ -+ sepgsql_relation_comment(RelationGetRelid(relation)); -+ - /* Next, verify that the relation type matches the intent */ - - switch (objtype) -@@ -651,6 +655,9 @@ CommentAttribute(List *qualname, char *comment) - errmsg("column \"%s\" of relation \"%s\" does not exist", - attrname, RelationGetRelationName(relation)))); - -+ /* SELinux checks */ -+ sepgsql_attribute_comment(RelationGetRelid(relation), attnum); -+ - /* Create the comment using the relation's oid */ - CreateComments(RelationGetRelid(relation), RelationRelationId, - (int32) attnum, comment); -@@ -704,6 +711,9 @@ CommentDatabase(List *qualname, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - database); - -+ /* SELinux checks */ -+ sepgsql_database_comment(oid); -+ - /* Call CreateSharedComments() to create/drop the comments */ - CreateSharedComments(oid, DatabaseRelationId, comment); - } -@@ -742,6 +752,9 @@ CommentTablespace(List *qualname, char *comment) - if (!pg_tablespace_ownercheck(oid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, tablespace); - -+ /* SELinux checks */ -+ sepgsql_tablespace_comment(oid); -+ - /* Call CreateSharedComments() to create/drop the comments */ - CreateSharedComments(oid, TableSpaceRelationId, comment); - } -@@ -774,6 +787,9 @@ CommentRole(List *qualname, char *comment) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be member of role \"%s\" to comment upon it", role))); - -+ /* SELinux checks */ -+ sepgsql_role_comment(oid); -+ - /* Call CreateSharedComments() to create/drop the comments */ - CreateSharedComments(oid, AuthIdRelationId, comment); - } -@@ -810,6 +826,9 @@ CommentNamespace(List *qualname, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, - namespace); - -+ /* SELinux checks */ -+ sepgsql_schema_comment(oid); -+ - /* Call CreateComments() to create/drop the comments */ - CreateComments(oid, NamespaceRelationId, 0, comment); - } -@@ -919,6 +938,9 @@ CommentRule(List *qualname, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - get_rel_name(reloid)); - -+ /* SELinux checks */ -+ sepgsql_rule_comment(reloid, rulename); -+ - /* Call CreateComments() to create/drop the comments */ - CreateComments(ruleoid, RewriteRelationId, 0, comment); - -@@ -953,6 +975,9 @@ CommentType(List *typename, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, - format_type_be(oid)); - -+ /* SELinux checks */ -+ sepgsql_type_comment(oid); -+ - /* Call CreateComments() to create/drop the comments */ - CreateComments(oid, TypeRelationId, 0, comment); - } -@@ -977,6 +1002,9 @@ CommentAggregate(List *aggregate, List *arguments, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - NameListToString(aggregate)); - -+ /* SELinux checks */ -+ sepgsql_proc_comment(oid); -+ - /* Call CreateComments() to create/drop the comments */ - CreateComments(oid, ProcedureRelationId, 0, comment); - } -@@ -1005,6 +1033,9 @@ CommentProc(List *function, List *arguments, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - NameListToString(function)); - -+ /* SELinux checks */ -+ sepgsql_proc_comment(oid); -+ - /* Call CreateComments() to create/drop the comments */ - CreateComments(oid, ProcedureRelationId, 0, comment); - } -@@ -1036,6 +1067,9 @@ CommentOperator(List *opername, List *arguments, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, - NameListToString(opername)); - -+ /* SELinux checks */ -+ sepgsql_operator_comment(oid); -+ - /* Call CreateComments() to create/drop the comments */ - CreateComments(oid, OperatorRelationId, 0, comment); - } -@@ -1080,6 +1114,9 @@ CommentTrigger(List *qualname, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(relation)); - -+ /* SELinux checks */ -+ sepgsql_trigger_comment(RelationGetRelid(relation), trigname); -+ - /* - * Fetch the trigger tuple from pg_trigger. There can be only one because - * of the unique index. -@@ -1153,6 +1190,9 @@ CommentConstraint(List *qualname, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(relation)); - -+ /* SELinux checks */ -+ sepgsql_constraint_comment(RelationGetRelid(relation), conName); -+ - conOid = GetConstraintByName(RelationGetRelid(relation), conName); - - /* Call CreateComments() to create/drop the comments */ -@@ -1188,6 +1228,9 @@ CommentConversion(List *qualname, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, - NameListToString(qualname)); - -+ /* SELinux checks */ -+ sepgsql_conversion_comment(conversionOid); -+ - /* Call CreateComments() to create/drop the comments */ - CreateComments(conversionOid, ConversionRelationId, 0, comment); - } -@@ -1304,6 +1347,9 @@ CommentOpClass(List *qualname, List *arguments, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, - NameListToString(qualname)); - -+ /* SELinux checks */ -+ sepgsql_opclass_comment(opcID); -+ - ReleaseSysCache(tuple); - - /* Call CreateComments() to create/drop the comments */ -@@ -1385,6 +1431,9 @@ CommentOpFamily(List *qualname, List *arguments, char *comment) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, - NameListToString(qualname)); - -+ /* SELinux checks */ -+ sepgsql_opfamily_comment(opfID); -+ - ReleaseSysCache(tuple); - - /* Call CreateComments() to create/drop the comments */ -@@ -1482,6 +1531,9 @@ CommentCast(List *qualname, List *arguments, char *comment) - format_type_be(sourcetypeid), - format_type_be(targettypeid)))); - -+ /* SELinux checks */ -+ sepgsql_cast_comment(sourcetypeid, targettypeid); -+ - ReleaseSysCache(tuple); - - /* Call CreateComments() to create/drop the comments */ -@@ -1499,6 +1551,8 @@ CommentTSParser(List *qualname, char *comment) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to comment on text search parser"))); -+ /* SELinux checks */ -+ sepgsql_ts_parser_comment(prsId); - - CreateComments(prsId, TSParserRelationId, 0, comment); - } -@@ -1513,6 +1567,8 @@ CommentTSDictionary(List *qualname, char *comment) - if (!pg_ts_dict_ownercheck(dictId, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, - NameListToString(qualname)); -+ /* SELinux checks */ -+ sepgsql_ts_dict_comment(dictId); - - CreateComments(dictId, TSDictionaryRelationId, 0, comment); - } -@@ -1528,6 +1584,8 @@ CommentTSTemplate(List *qualname, char *comment) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to comment on text search template"))); -+ /* SELinux checks */ -+ sepgsql_ts_template_comment(tmplId); - - CreateComments(tmplId, TSTemplateRelationId, 0, comment); - } -@@ -1542,6 +1600,8 @@ CommentTSConfiguration(List *qualname, char *comment) - if (!pg_ts_config_ownercheck(cfgId, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, - NameListToString(qualname)); -+ /* SELinux checks */ -+ sepgsql_ts_config_comment(cfgId); - - CreateComments(cfgId, TSConfigRelationId, 0, comment); - } -diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c -index 2e5a7df..9647622 100644 ---- a/src/backend/commands/conversioncmds.c -+++ b/src/backend/commands/conversioncmds.c -@@ -24,6 +24,7 @@ - #include "mb/pg_wchar.h" - #include "miscadmin.h" - #include "parser/parse_func.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -45,6 +46,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) - int from_encoding; - int to_encoding; - Oid funcoid; -+ Oid secid; - const char *from_encoding_name = stmt->for_encoding_name; - const char *to_encoding_name = stmt->to_encoding_name; - List *func_name = stmt->func_name; -@@ -96,6 +98,10 @@ CreateConversionCommand(CreateConversionStmt *stmt) - aclcheck_error(aclresult, ACL_KIND_PROC, - NameListToString(func_name)); - -+ /* SELinux checks */ -+ secid = sepgsql_conversion_create(conversion_name, -+ namespaceId, funcoid); -+ - /* - * Check that the conversion function is suitable for the requested source - * and target encodings. We do that by calling the function with an empty -@@ -114,7 +120,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) - * name) - */ - ConversionCreate(conversion_name, namespaceId, GetUserId(), -- from_encoding, to_encoding, funcoid, stmt->def); -+ from_encoding, to_encoding, funcoid, stmt->def, secid); - } - - /* -@@ -174,6 +180,9 @@ DropConversionsCommand(DropStmt *drop) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, - NameStr(con->conname)); - -+ /* SELinux checks */ -+ sepgsql_conversion_drop(conversionOid, false); -+ - object.classId = ConversionRelationId; - object.objectId = conversionOid; - object.objectSubId = 0; -@@ -235,6 +244,9 @@ RenameConversion(List *name, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux checks */ -+ sepgsql_conversion_alter_rename(conversionOid, newname); -+ - /* rename */ - namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); - simple_heap_update(rel, &tup->t_self, tup); -@@ -329,6 +341,8 @@ AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(convForm->connamespace)); - } -+ /* SELinux checks */ -+ sepgsql_conversion_alter(HeapTupleGetOid(tup)); - - /* - * Modify the owner --- okay to scribble on tup because it's a copy -diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c -index a6e1d35..50921ec 100644 ---- a/src/backend/commands/copy.c -+++ b/src/backend/commands/copy.c -@@ -22,7 +22,10 @@ - - #include "access/heapam.h" - #include "access/xact.h" -+#include "access/sysattr.h" -+#include "catalog/heap.h" - #include "catalog/namespace.h" -+#include "catalog/pg_seclabel.h" - #include "catalog/pg_type.h" - #include "commands/copy.h" - #include "commands/defrem.h" -@@ -35,6 +38,7 @@ - #include "optimizer/planner.h" - #include "parser/parse_relation.h" - #include "rewrite/rewriteHandler.h" -+#include "sepgsql/hooks.h" - #include "storage/fd.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" -@@ -161,6 +165,12 @@ typedef struct CopyStateData - char *raw_buf; - int raw_buf_index; /* next byte to process */ - int raw_buf_len; /* total # of bytes stored */ -+ -+ /* -+ * Dump/Restore support for security_label -+ */ -+ FmgrInfo seclabel_out_function; -+ bool seclabel_force_quot; - } CopyStateData; - - typedef CopyStateData *CopyState; -@@ -244,7 +254,7 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0"; - /* non-export function prototypes */ - static void DoCopyTo(CopyState cstate); - static void CopyTo(CopyState cstate); --static void CopyOneRowTo(CopyState cstate, Oid tupleOid, -+static void CopyOneRowTo(CopyState cstate, HeapTuple tuple, - Datum *values, bool *nulls); - static void CopyFrom(CopyState cstate); - static bool CopyReadLine(CopyState cstate); -@@ -986,8 +996,21 @@ DoCopy(const CopyStmt *stmt, const char *queryString) - errhint("Anyone can COPY to stdout or from stdin. " - "psql's \\copy command also works for anyone."))); - -+ /* SELinux checks on file COPY */ -+ if (stmt->filename) -+ { -+ if (is_from) -+ sepgsql_file_read(stmt->filename); -+ else -+ sepgsql_file_write(stmt->filename, true); -+ } -+ - if (stmt->relation) - { -+ Bitmapset *columnsSet = NULL; -+ List *attnums; -+ ListCell *cur; -+ - Assert(!stmt->query); - cstate->queryDesc = NULL; - -@@ -998,16 +1021,20 @@ DoCopy(const CopyStmt *stmt, const char *queryString) - tupDesc = RelationGetDescr(cstate->rel); - - /* Check relation permissions. */ -+ attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); -+ foreach(cur, attnums) -+ { -+ int index = lfirst_int(cur) -+ - FirstLowInvalidHeapAttributeNumber; -+ columnsSet = bms_add_member(columnsSet, index); -+ } -+ - relPerms = pg_class_aclmask(RelationGetRelid(cstate->rel), GetUserId(), - required_access, ACLMASK_ALL); - remainingPerms = required_access & ~relPerms; - if (remainingPerms != 0) - { - /* We don't have table permissions, check per-column permissions */ -- List *attnums; -- ListCell *cur; -- -- attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); - foreach(cur, attnums) - { - int attnum = lfirst_int(cur); -@@ -1021,6 +1048,13 @@ DoCopy(const CopyStmt *stmt, const char *queryString) - } - } - -+ if (is_from) -+ sepgsql_relation_perms(RelationGetRelid(cstate->rel), -+ ACL_INSERT, NULL, columnsSet, true); -+ else -+ sepgsql_relation_perms(RelationGetRelid(cstate->rel), -+ ACL_SELECT, columnsSet, NULL, true); -+ - /* check read-only transaction */ - if (XactReadOnly && is_from && !cstate->rel->rd_islocaltemp) - PreventCommandIfReadOnly("COPY FROM"); -@@ -1130,11 +1164,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString) - int attnum = lfirst_int(cur); - - if (!list_member_int(cstate->attnumlist, attnum)) -+ { -+ Form_pg_attribute attForm; -+ -+ if (attnum > 0) -+ attForm = tupDesc->attrs[attnum - 1]; -+ else -+ attForm = SystemAttributeDefinition(attnum, true, true); -+ - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", -- NameStr(tupDesc->attrs[attnum - 1]->attname)))); -- cstate->force_quote_flags[attnum - 1] = true; -+ NameStr(attForm->attname)))); -+ } -+ -+ if (attnum == SecurityLabelAttributeNumber) -+ cstate->seclabel_force_quot = true; -+ else -+ cstate->force_quote_flags[attnum - 1] = true; - } - } - -@@ -1152,10 +1199,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString) - int attnum = lfirst_int(cur); - - if (!list_member_int(cstate->attnumlist, attnum)) -+ { -+ Form_pg_attribute attForm; -+ -+ if (attnum > 0) -+ attForm = tupDesc->attrs[attnum - 1]; -+ else -+ attForm = SystemAttributeDefinition(attnum, -+ tupDesc->tdhasoid, -+ tupDesc->tdhassecid); -+ - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", -- NameStr(tupDesc->attrs[attnum - 1]->attname)))); -+ NameStr(attForm->attname)))); -+ } -+ /* ignore system columns, if specified */ -+ if (attnum <= 0) -+ continue; - cstate->force_notnull_flags[attnum - 1] = true; - } - } -@@ -1347,16 +1408,29 @@ CopyTo(CopyState cstate) - int attnum = lfirst_int(cur); - Oid out_func_oid; - bool isvarlena; -+ FmgrInfo *out_fmgr; -+ Form_pg_attribute attForm; -+ -+ if (attnum == SecurityLabelAttributeNumber) -+ { -+ attForm = SystemAttributeDefinition(attnum, true, true); -+ out_fmgr = &cstate->seclabel_out_function; -+ } -+ else -+ { -+ attForm = attr[attnum - 1]; -+ out_fmgr = &cstate->out_functions[attnum - 1]; -+ } - - if (cstate->binary) -- getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, -+ getTypeBinaryOutputInfo(attForm->atttypid, - &out_func_oid, - &isvarlena); - else -- getTypeOutputInfo(attr[attnum - 1]->atttypid, -+ getTypeOutputInfo(attForm->atttypid, - &out_func_oid, - &isvarlena); -- fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); -+ fmgr_info(out_func_oid, out_fmgr); - } - - /* -@@ -1411,7 +1485,17 @@ CopyTo(CopyState cstate) - CopySendChar(cstate, cstate->delim[0]); - hdr_delim = true; - -- colname = NameStr(attr[attnum - 1]->attname); -+ if (SystemAttributeWritable(attnum, -+ tupDesc->tdhasoid, -+ tupDesc->tdhassecid)) -+ { -+ Form_pg_attribute attForm -+ = SystemAttributeDefinition(attnum, true, true); -+ -+ colname = NameStr(attForm->attname); -+ } -+ else -+ colname = NameStr(attr[attnum - 1]->attname); - - CopyAttributeOutCSV(cstate, colname, false, - list_length(cstate->attnumlist) == 1); -@@ -1437,11 +1521,15 @@ CopyTo(CopyState cstate) - { - CHECK_FOR_INTERRUPTS(); - -+ /* Row-level access control policy */ -+ if (!sepgsql_rowlv_copyto(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); -@@ -1467,13 +1555,17 @@ CopyTo(CopyState cstate) - * Emit one row during CopyTo(). - */ - static void --CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) -+CopyOneRowTo(CopyState cstate, HeapTuple tuple, Datum *values, bool *nulls) - { - bool need_delim = false; - FmgrInfo *out_functions = cstate->out_functions; - MemoryContext oldcontext; - ListCell *cur; - char *string; -+ Oid tupleOid = InvalidOid; -+ -+ if (HeapTupleIsValid(tuple)) -+ tupleOid = HeapTupleGetOid(tuple); - - MemoryContextReset(cstate->rowcontext); - oldcontext = MemoryContextSwitchTo(cstate->rowcontext); -@@ -1506,8 +1598,10 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) - foreach(cur, cstate->attnumlist) - { - int attnum = lfirst_int(cur); -- Datum value = values[attnum - 1]; -- bool isnull = nulls[attnum - 1]; -+ Datum value; -+ bool isnull; -+ bool force_quot; -+ FmgrInfo *out_fmgr; - - if (!cstate->binary) - { -@@ -1516,6 +1610,21 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) - need_delim = true; - } - -+ if (attnum == SecurityLabelAttributeNumber) -+ { -+ value = seclabelSysattOutput(RelationGetRelid(cstate->rel), tuple); -+ isnull = false; -+ force_quot = cstate->seclabel_force_quot; -+ out_fmgr = &cstate->seclabel_out_function; -+ } -+ else -+ { -+ value = values[attnum - 1]; -+ isnull = nulls[attnum - 1]; -+ force_quot = cstate->force_quote_flags[attnum - 1]; -+ out_fmgr = &out_functions[attnum - 1]; -+ } -+ - if (isnull) - { - if (!cstate->binary) -@@ -1527,11 +1636,9 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) - { - if (!cstate->binary) - { -- string = OutputFunctionCall(&out_functions[attnum - 1], -- value); -+ string = OutputFunctionCall(out_fmgr, value); - if (cstate->csv_mode) -- CopyAttributeOutCSV(cstate, string, -- cstate->force_quote_flags[attnum - 1], -+ CopyAttributeOutCSV(cstate, string, force_quot, - list_length(cstate->attnumlist) == 1); - else - CopyAttributeOutText(cstate, string); -@@ -1540,8 +1647,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) - { - bytea *outputbytes; - -- outputbytes = SendFunctionCall(&out_functions[attnum - 1], -- value); -+ outputbytes = SendFunctionCall(out_fmgr, value); - CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); - CopySendData(cstate, VARDATA(outputbytes), - VARSIZE(outputbytes) - VARHDRSZ); -@@ -1675,8 +1781,10 @@ CopyFrom(CopyState cstate) - num_defaults; - FmgrInfo *in_functions; - FmgrInfo oid_in_function; -+ FmgrInfo seclabel_in_function; - Oid *typioparams; - Oid oid_typioparam; -+ Oid seclabel_typioparam; - int attnum; - int i; - Oid in_func_oid; -@@ -1919,6 +2027,19 @@ CopyFrom(CopyState cstate) - fmgr_info(in_func_oid, &oid_in_function); - } - -+ if (list_member_int(cstate->attnumlist, SecurityLabelAttributeNumber)) -+ { -+ if (!cstate->binary) -+ getTypeInputInfo(TEXTOID, &in_func_oid, &seclabel_typioparam); -+ else -+ getTypeBinaryInputInfo(TEXTOID, &in_func_oid, &seclabel_typioparam); -+ -+ fmgr_info(in_func_oid, &seclabel_in_function); -+ } -+ -+ -+ -+ - values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); - nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); - -@@ -1953,6 +2074,7 @@ CopyFrom(CopyState cstate) - { - bool skip_tuple; - Oid loaded_oid = InvalidOid; -+ Oid loaded_secid = InvalidOid; - - CHECK_FOR_INTERRUPTS(); - -@@ -2024,14 +2146,20 @@ CopyFrom(CopyState cstate) - /* Loop to read the user attributes on the line. */ - foreach(cur, cstate->attnumlist) - { -+ Form_pg_attribute attForm; - int attnum = lfirst_int(cur); - int m = attnum - 1; - -+ if (attnum == SecurityLabelAttributeNumber) -+ attForm = SystemAttributeDefinition(attnum, true, true); -+ else -+ attForm = attr[m]; -+ - if (fieldno >= fldct) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("missing data for column \"%s\"", -- NameStr(attr[m]->attname)))); -+ NameStr(attForm->attname)))); - string = field_strings[fieldno++]; - - if (cstate->csv_mode && string == NULL && -@@ -2041,14 +2169,38 @@ CopyFrom(CopyState cstate) - string = cstate->null_print; - } - -- cstate->cur_attname = NameStr(attr[m]->attname); -+ cstate->cur_attname = NameStr(attForm->attname); - cstate->cur_attval = string; -- values[m] = InputFunctionCall(&in_functions[m], -- string, -- typioparams[m], -- attr[m]->atttypmod); -- if (string != NULL) -- nulls[m] = false; -+ -+ if (attnum == SecurityLabelAttributeNumber) -+ { -+ if (string && !ignore_security_label_input) -+ { -+ Datum datum = InputFunctionCall(&seclabel_in_function, -+ string, -+ seclabel_typioparam, -+ attForm->atttypmod); -+ loaded_secid -+ = seclabelTransInput(RelationGetRelid(cstate->rel), -+ TextDatumGetCString(datum)); -+ } -+ } -+ else -+ { -+ if (cstate->csv_mode && string == NULL && -+ cstate->force_notnull_flags[m]) -+ { -+ /* Go ahead and read the NULL string */ -+ string = cstate->null_print; -+ } -+ -+ values[m] = InputFunctionCall(&in_functions[m], -+ string, -+ typioparams[m], -+ attForm->atttypmod); -+ if (string != NULL) -+ nulls[m] = false; -+ } - cstate->cur_attname = NULL; - cstate->cur_attval = NULL; - } -@@ -2094,17 +2246,38 @@ CopyFrom(CopyState cstate) - i = 0; - foreach(cur, cstate->attnumlist) - { -+ Form_pg_attribute attForm; - int attnum = lfirst_int(cur); - int m = attnum - 1; - -- cstate->cur_attname = NameStr(attr[m]->attname); -+ if (attnum == SecurityLabelAttributeNumber) -+ attForm = SystemAttributeDefinition(attnum, true, true); -+ else -+ attForm = attr[m]; -+ -+ cstate->cur_attname = NameStr(attForm->attname); - i++; -- values[m] = CopyReadBinaryAttribute(cstate, -- i, -- &in_functions[m], -- typioparams[m], -- attr[m]->atttypmod, -- &nulls[m]); -+ -+ if (attnum == SecurityLabelAttributeNumber) -+ { -+ Datum datum = CopyReadBinaryAttribute(cstate, i, -+ &seclabel_in_function, -+ seclabel_typioparam, -+ attForm->atttypmod, -+ &isnull); -+ if (!isnull && !ignore_security_label_input) -+ loaded_secid -+ = seclabelTransInput(RelationGetRelid(cstate->rel), -+ TextDatumGetCString(datum)); -+ } -+ else -+ { -+ values[m] = CopyReadBinaryAttribute(cstate, i, -+ &in_functions[m], -+ typioparams[m], -+ attr[m]->atttypmod, -+ &nulls[m]); -+ } - cstate->cur_attname = NULL; - } - } -@@ -2125,6 +2298,8 @@ CopyFrom(CopyState cstate) - - if (cstate->oids && file_has_oids) - HeapTupleSetOid(tuple, loaded_oid); -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, loaded_secid); - - /* Triggers and stuff need to be invoked in query context. */ - MemoryContextSwitchTo(oldcontext); -@@ -2149,6 +2324,9 @@ CopyFrom(CopyState cstate) - } - - if (!skip_tuple) -+ sepgsql_tuple_insert(cstate->rel, tuple); -+ -+ if (!skip_tuple) - { - List *recheckIndexes = NIL; - -@@ -3435,6 +3613,17 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) - } - if (attnum == InvalidAttrNumber) - { -+ Form_pg_attribute attForm; -+ bool hasoid = tupDesc->tdhasoid; -+ bool hassecid = tupDesc->tdhassecid; -+ -+ attForm = SystemAttributeByName(name, hasoid, hassecid); -+ if (attForm && -+ SystemAttributeWritable(attForm->attnum, hasoid, hassecid)) -+ attnum = attForm->attnum; -+ } -+ if (attnum == InvalidAttrNumber) -+ { - if (rel != NULL) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), -@@ -3482,7 +3671,7 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self) - slot_getallattrs(slot); - - /* And send the data */ -- CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); -+ CopyOneRowTo(cstate, slot->tts_tuple, slot->tts_values, slot->tts_isnull); - } - - /* -diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c -index 3d54324..3687922 100644 ---- a/src/backend/commands/dbcommands.c -+++ b/src/backend/commands/dbcommands.c -@@ -35,6 +35,8 @@ - #include "catalog/pg_authid.h" - #include "catalog/pg_database.h" - #include "catalog/pg_db_role_setting.h" -+#include "catalog/pg_seclabel.h" -+#include "catalog/pg_shdescription.h" - #include "catalog/pg_tablespace.h" - #include "commands/comment.h" - #include "commands/dbcommands.h" -@@ -43,6 +45,7 @@ - #include "miscadmin.h" - #include "pgstat.h" - #include "postmaster/bgwriter.h" -+#include "sepgsql/hooks.h" - #include "storage/bufmgr.h" - #include "storage/fd.h" - #include "storage/lmgr.h" -@@ -111,6 +114,7 @@ createdb(const CreatedbStmt *stmt) - Datum new_record[Natts_pg_database]; - bool new_record_nulls[Natts_pg_database]; - Oid dboid; -+ Oid dbsecid; - Oid datdba; - ListCell *option; - DefElem *dtablespacename = NULL; -@@ -486,6 +490,9 @@ createdb(const CreatedbStmt *stmt) - /* Note there is no additional permission check in this path */ - } - -+ /* SELinux permission checks */ -+ dbsecid = sepgsql_database_create(dbname, src_dboid); -+ - /* - * Check for db name conflict. This is just to give a more friendly error - * message than "unique index violation". There's a race condition but -@@ -560,6 +567,8 @@ createdb(const CreatedbStmt *stmt) - new_record, new_record_nulls); - - HeapTupleSetOid(tuple, dboid); -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, dbsecid); - - simple_heap_insert(pg_database_rel, tuple); - -@@ -576,6 +585,9 @@ createdb(const CreatedbStmt *stmt) - /* Create pg_shdepend entries for objects within database */ - copyTemplateDependencies(src_dboid, dboid); - -+ /* Create pg_seclabel entries for objects within database */ -+ seclabelOnCreateDatabase(src_dboid, dboid); -+ - /* - * Force a checkpoint before starting the copy. This will force dirty - * buffers out to disk, to ensure source database is up-to-date on disk -@@ -777,6 +789,9 @@ dropdb(const char *dbname, bool missing_ok) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - dbname); - -+ /* SELinux checks */ -+ sepgsql_database_drop(db_id, false); -+ - /* - * Disallow dropping a DB that is marked istemplate. This is just to - * prevent people from accidentally dropping template0 or template1; they -@@ -833,6 +848,11 @@ dropdb(const char *dbname, bool missing_ok) - dropDatabaseDependencies(db_id); - - /* -+ * Remove pg_seclabel entries for the database -+ */ -+ seclabelOnDropDatabase(db_id); -+ -+ /* - * Drop pages for this database that are in the shared buffer cache. This - * is important to ensure that no remaining backend tries to write out a - * dirty buffer to the dead database later... -@@ -915,6 +935,9 @@ RenameDatabase(const char *oldname, const char *newname) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to rename database"))); - -+ /* SELinux checks */ -+ sepgsql_database_alter(db_id); -+ - /* - * Make sure the new name doesn't exist. See notes for same error in - * CREATE DATABASE. -@@ -1053,6 +1076,9 @@ movedb(const char *dbname, const char *tblspcname) - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("pg_global cannot be used as default tablespace"))); - -+ /* SELinux checks */ -+ sepgsql_database_alter(db_id); -+ - /* - * No-op if same tablespace - */ -@@ -1369,6 +1395,9 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - stmt->dbname); - -+ /* SELinux checks */ -+ sepgsql_database_alter(HeapTupleGetOid(tuple)); -+ - /* - * Build an updated tuple, perusing the information just obtained - */ -@@ -1419,6 +1448,9 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - stmt->dbname); - -+ /* SELinux checks */ -+ sepgsql_database_alter(datid); -+ - AlterSetting(datid, InvalidOid, stmt->setstmt); - - UnlockSharedObject(DatabaseRelationId, datid, 0, AccessShareLock); -@@ -1494,6 +1526,9 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to change owner of database"))); - -+ /* SELinux checks */ -+ sepgsql_database_alter(HeapTupleGetOid(tuple)); -+ - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); - -@@ -1533,6 +1568,58 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) - heap_close(rel, NoLock); - } - -+/* -+ * ALTER DATABASE SECURITY LABEL TO -+ */ -+void -+AlterDatabaseSecLabel(const char *dbname, char *new_label) -+{ -+ Relation rel; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ Oid databaseId; -+ Oid securityId; -+ -+ /* Fetch the old tuple */ -+ rel = heap_open(DatabaseRelationId, RowExclusiveLock); -+ ScanKeyInit(&skey, -+ Anum_pg_database_datname, -+ BTEqualStrategyNumber, F_NAMEEQ, -+ NameGetDatum(dbname)); -+ scan = systable_beginscan(rel, DatabaseNameIndexId, true, -+ SnapshotNow, 1, &skey); -+ oldtup = systable_getnext(scan); -+ if (!HeapTupleIsValid(oldtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_DATABASE), -+ errmsg("database \"%s\" does not exist", dbname))); -+ -+ newtup = heap_copytuple(oldtup); -+ -+ systable_endscan(scan); -+ -+ databaseId = HeapTupleGetOid(newtup); -+ -+ /* DAC permission checks */ -+ if (!pg_database_ownercheck(databaseId, GetUserId())) -+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); -+ -+ /* SELinux checks */ -+ securityId = sepgsql_database_relabel(databaseId, new_label); -+ -+ /* Update it */ -+ HeapTupleSetSecid(newtup, securityId); -+ -+ simple_heap_update(rel, &newtup->t_self, newtup); -+ -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ -+ heap_close(rel, RowExclusiveLock); -+} - - /* - * Helper functions -diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c -index e8dba94..4c4fc36 100644 ---- a/src/backend/commands/explain.c -+++ b/src/backend/commands/explain.c -@@ -257,7 +257,7 @@ ExplainResultDesc(ExplainStmt *stmt) - } - - /* Need a tuple descriptor representing a single TEXT or XML column */ -- tupdesc = CreateTemplateTupleDesc(1, false); -+ tupdesc = CreateTemplateTupleDesc(1, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN", - xml ? XMLOID : TEXTOID, -1, 0); - return tupdesc; -diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c -index abbe731..b36c928 100644 ---- a/src/backend/commands/foreigncmds.c -+++ b/src/backend/commands/foreigncmds.c -@@ -27,6 +27,7 @@ - #include "foreign/foreign.h" - #include "miscadmin.h" - #include "parser/parse_func.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -230,6 +231,9 @@ AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId) - fdwId = HeapTupleGetOid(tup); - form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup); - -+ /* SELinux checks */ -+ sepgsql_fdw_alter(fdwId, InvalidOid); -+ - if (form->fdwowner != newOwnerId) - { - form->fdwowner = newOwnerId; -@@ -294,6 +298,8 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); - } - } -+ /* SELinux checks */ -+ sepgsql_fserver_alter(srvId); - - form->srvowner = newOwnerId; - -@@ -339,6 +345,7 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) - Oid fdwvalidator; - Datum fdwoptions; - Oid ownerId; -+ Oid securityId; - - /* Must be super user */ - if (!superuser()) -@@ -391,8 +398,13 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) - else - nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; - -+ /* SELinux checks */ -+ securityId = sepgsql_fdw_create(stmt->fdwname, fdwvalidator); -+ - tuple = heap_form_tuple(rel->rd_att, values, nulls); - -+ HeapTupleSetSecid(tuple, securityId); -+ - fdwId = simple_heap_insert(rel, tuple); - CatalogUpdateIndexes(rel, tuple); - -@@ -511,6 +523,8 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt) - - repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; - } -+ /* SELinux checks */ -+ sepgsql_fdw_alter(fdwId, fdwvalidator); - - /* Everything looks good - update the tuple */ - -@@ -559,6 +573,8 @@ RemoveForeignDataWrapper(DropFdwStmt *stmt) - stmt->fdwname))); - return; - } -+ /* SELinux checks */ -+ sepgsql_fdw_drop(fdwId, false); - - /* - * Do the deletion -@@ -608,6 +624,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt) - HeapTuple tuple; - Oid srvId; - Oid ownerId; -+ Oid securityId; - AclResult aclresult; - ObjectAddress myself; - ObjectAddress referenced; -@@ -635,6 +652,9 @@ CreateForeignServer(CreateForeignServerStmt *stmt) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); - -+ /* SELinux checks */ -+ securityId = sepgsql_fserver_create(stmt->servername, fdw->fdwid); -+ - /* - * Insert tuple into pg_foreign_server. - */ -@@ -678,6 +698,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt) - - tuple = heap_form_tuple(rel->rd_att, values, nulls); - -+ HeapTupleSetSecid(tuple, securityId); -+ - srvId = simple_heap_insert(rel, tuple); - - CatalogUpdateIndexes(rel, tuple); -@@ -732,6 +754,9 @@ AlterForeignServer(AlterForeignServerStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, - stmt->servername); - -+ /* SELinux checks */ -+ sepgsql_fserver_alter(srvId); -+ - memset(repl_val, 0, sizeof(repl_val)); - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); -@@ -823,6 +848,8 @@ RemoveForeignServer(DropForeignServerStmt *stmt) - if (!pg_foreign_server_ownercheck(srvId, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, - stmt->servername); -+ /* SELinux checks */ -+ sepgsql_fserver_drop(srvId, false); - - object.classId = ForeignServerRelationId; - object.objectId = srvId; -@@ -896,6 +923,7 @@ CreateUserMapping(CreateUserMappingStmt *stmt) - HeapTuple tuple; - Oid useId; - Oid umId; -+ Oid securityId; - ObjectAddress myself; - ObjectAddress referenced; - ForeignServer *srv; -@@ -908,6 +936,9 @@ CreateUserMapping(CreateUserMappingStmt *stmt) - - user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername); - -+ /* SELinux checks */ -+ securityId = sepgsql_user_mapping_create(useId, srv->serverid); -+ - /* - * Check that the user mapping is unique within server. - */ -@@ -947,6 +978,8 @@ CreateUserMapping(CreateUserMappingStmt *stmt) - - tuple = heap_form_tuple(rel->rd_att, values, nulls); - -+ HeapTupleSetSecid(tuple, securityId); -+ - umId = simple_heap_insert(rel, tuple); - - CatalogUpdateIndexes(rel, tuple); -@@ -1000,6 +1033,9 @@ AlterUserMapping(AlterUserMappingStmt *stmt) - - user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername); - -+ /* SELinux checks */ -+ sepgsql_user_mapping_alter(umId); -+ - tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId)); - - if (!HeapTupleIsValid(tp)) -@@ -1114,6 +1150,9 @@ RemoveUserMapping(DropUserMappingStmt *stmt) - - user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername); - -+ /* SELinux checks */ -+ sepgsql_user_mapping_drop(umId, false); -+ - /* - * Do the deletion - */ -diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c -index 26a3a52..e2b8683 100644 ---- a/src/backend/commands/functioncmds.c -+++ b/src/backend/commands/functioncmds.c -@@ -43,6 +43,7 @@ - #include "catalog/pg_namespace.h" - #include "catalog/pg_proc.h" - #include "catalog/pg_proc_fn.h" -+#include "catalog/pg_seclabel.h" - #include "catalog/pg_type.h" - #include "catalog/pg_type_fn.h" - #include "commands/defrem.h" -@@ -53,6 +54,7 @@ - #include "parser/parse_expr.h" - #include "parser/parse_func.h" - #include "parser/parse_type.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -788,6 +790,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) - ArrayType *parameterNames; - List *parameterDefaults; - Oid requiredResultType; -+ Oid replacedFunc = InvalidOid; -+ Oid prosecid; - bool isWindowFunc, - isStrict, - security; -@@ -933,6 +937,18 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) - errmsg("ROWS is not applicable when function does not return a set"))); - - /* -+ * SELinux checks -+ */ -+ if (stmt->replace) -+ replacedFunc = GetSysCacheOid3(PROCNAMEARGSNSP, -+ PointerGetDatum(funcname), -+ PointerGetDatum(parameterTypes), -+ ObjectIdGetDatum(namespaceId)); -+ -+ prosecid = sepgsql_proc_create(funcname, replacedFunc, -+ namespaceId, languageOid); -+ -+ /* - * And now that we have all the parameters, and know we're permitted to do - * so, go ahead and create the function. - */ -@@ -957,7 +973,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) - parameterDefaults, - PointerGetDatum(proconfig), - procost, -- prorows); -+ prorows, -+ prosecid); - } - - -@@ -999,6 +1016,9 @@ RemoveFunction(RemoveFuncStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - NameListToString(functionName)); - -+ /* SELinux checks */ -+ sepgsql_proc_drop(funcOid, false); -+ - if (((Form_pg_proc) GETSTRUCT(tup))->proisagg) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -@@ -1135,6 +1155,9 @@ RenameFunction(List *name, List *argtypes, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux checks */ -+ sepgsql_proc_alter_rename(procOid, newname); -+ - /* rename */ - namestrcpy(&(procForm->proname), newname); - simple_heap_update(rel, &tup->t_self, tup); -@@ -1239,6 +1262,8 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(procForm->pronamespace)); - } -+ /* SELinux checks */ -+ sepgsql_proc_alter(procOid); - - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); -@@ -1277,6 +1302,49 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) - } - - /* -+ * ALTER FUNCTION f(...) SECURITY LABEL TO -+ */ -+void -+AlterFunctionSecLabel(List *name, List *argtypes, bool isagg, char *new_label) -+{ -+ Relation rel; -+ HeapTuple tuple; -+ Oid procOid; -+ Oid securityId; -+ -+ /* open pg_proc system catalog */ -+ rel = heap_open(ProcedureRelationId, RowExclusiveLock); -+ -+ /* get function OID */ -+ if (isagg) -+ procOid = LookupAggNameTypeNames(name, argtypes, false); -+ else -+ procOid = LookupFuncNameTypeNames(name, argtypes, false); -+ -+ tuple = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for function %u", procOid); -+ -+ /* Must be owner */ -+ if (!pg_proc_ownercheck(procOid, GetUserId())) -+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, -+ get_func_name(procOid)); -+ -+ /* SELinux checks */ -+ securityId = sepgsql_proc_relabel(procOid, new_label); -+ -+ /* Update it */ -+ HeapTupleSetSecid(tuple, securityId); -+ -+ simple_heap_update(rel, &tuple->t_self, tuple); -+ CatalogUpdateIndexes(rel, tuple); -+ -+ heap_freetuple(tuple); -+ -+ heap_close(rel, RowExclusiveLock); -+} -+ -+/* - * Implements the ALTER FUNCTION utility command (except for the - * RENAME and OWNER clauses, which are handled as part of the generic - * ALTER framework). -@@ -1313,6 +1381,9 @@ AlterFunction(AlterFunctionStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - NameListToString(stmt->func->funcname)); - -+ /* SELinux checks */ -+ sepgsql_proc_alter(funcOid); -+ - if (procForm->proisagg) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -@@ -1659,6 +1730,9 @@ CreateCast(CreateCastStmt *stmt) - errmsg("array data types are not binary-compatible"))); - } - -+ /* SELinux checks */ -+ sepgsql_cast_create(sourcetypeid, targettypeid, castmethod, funcid); -+ - /* - * Allow source and target types to be same only for length coercion - * functions. We assume a multi-arg function does length coercion. -@@ -1795,6 +1869,9 @@ DropCast(DropCastStmt *stmt) - format_type_be(sourcetypeid), - format_type_be(targettypeid)))); - -+ /* SELinux checks */ -+ sepgsql_cast_drop(sourcetypeid, targettypeid, false); -+ - /* - * Do the deletion - */ -@@ -1873,6 +1950,9 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, - /* get schema OID and check its permissions */ - nspOid = LookupCreationNamespace(newschema); - -+ /* SELinux checks */ -+ sepgsql_proc_alter_schema(procOid, nspOid); -+ - if (oldNspOid == nspOid) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_FUNCTION), -diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c -index a129511..6c2dbc4 100644 ---- a/src/backend/commands/indexcmds.c -+++ b/src/backend/commands/indexcmds.c -@@ -40,6 +40,7 @@ - #include "parser/parse_func.h" - #include "parser/parse_oper.h" - #include "parser/parsetree.h" -+#include "sepgsql/hooks.h" - #include "storage/lmgr.h" - #include "storage/proc.h" - #include "storage/procarray.h" -@@ -243,6 +244,10 @@ DefineIndex(RangeVar *heapRelation, - get_tablespace_name(tablespaceId)); - } - -+ /* SELinux checks */ -+ if (check_rights) -+ sepgsql_index_create(relationId, namespaceId); -+ - /* - * Force shared indexes into the pg_global tablespace. This is a bit of a - * hack but seems simpler than marking them in the BKI commands. On the -@@ -364,7 +369,9 @@ DefineIndex(RangeVar *heapRelation, - errmsg("primary keys cannot be expressions"))); - - /* System attributes are never null, so no problem */ -- if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids)) -+ if (SystemAttributeByName(key->name, -+ rel->rd_rel->relhasoids, -+ rel->rd_rel->relhassecids)) - continue; - - atttuple = SearchSysCacheAttName(relationId, key->name); -@@ -1600,6 +1607,9 @@ ReindexIndex(RangeVar *indexRelation) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - indexRelation->relname); - -+ /* SELinux checks */ -+ sepgsql_index_reindex(indOid); -+ - ReleaseSysCache(tuple); - - reindex_index(indOid, false); -@@ -1632,6 +1642,9 @@ ReindexTable(RangeVar *relation) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - relation->relname); - -+ /* SELinux checks */ -+ sepgsql_relation_reindex(heapOid); -+ - ReleaseSysCache(tuple); - - if (!reindex_relation(heapOid, true, 0)) -@@ -1670,6 +1683,9 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - databaseName); - -+ /* SELinux checks */ -+ sepgsql_database_reindex(MyDatabaseId); -+ - /* - * Create a memory context that will survive forced transaction commits we - * do below. Since it is a child of PortalContext, it will go away -diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c -index 34d657c..d3c69a3 100644 ---- a/src/backend/commands/lockcmds.c -+++ b/src/backend/commands/lockcmds.c -@@ -20,6 +20,7 @@ - #include "commands/lockcmds.h" - #include "miscadmin.h" - #include "parser/parse_clause.h" -+#include "sepgsql/hooks.h" - #include "storage/lmgr.h" - #include "utils/acl.h" - #include "utils/lsyscache.h" -@@ -149,6 +150,9 @@ LockTableRecurse(Oid reloid, RangeVar *rv, - errmsg("\"%s\" is not a table", - RelationGetRelationName(rel)))); - -+ /* SELinux checks */ -+ sepgsql_relation_lock(rel); -+ - /* - * If requested, recurse to children. We use find_inheritance_children - * not find_all_inheritors to avoid taking locks far in advance of -diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c -index cae1a31..c946c5c 100644 ---- a/src/backend/commands/opclasscmds.c -+++ b/src/backend/commands/opclasscmds.c -@@ -35,6 +35,7 @@ - #include "parser/parse_func.h" - #include "parser/parse_oper.h" - #include "parser/parse_type.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -164,7 +165,8 @@ OpClassCacheLookup(Oid amID, List *opclassname) - * Caller must have done permissions checks etc. already. - */ - static Oid --CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) -+CreateOpFamily(char *amname, char *opfname, -+ Oid namespaceoid, Oid amoid, Oid securityId) - { - Oid opfamilyoid; - Relation rel; -@@ -204,6 +206,8 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) - - tup = heap_form_tuple(rel->rd_att, values, nulls); - -+ HeapTupleSetSecid(tup, securityId); -+ - opfamilyoid = simple_heap_insert(rel, tup); - - CatalogUpdateIndexes(rel, tup); -@@ -369,11 +373,16 @@ DefineOpClass(CreateOpClassStmt *stmt) - } - else - { -+ Oid securityId; -+ -+ /* SELinux checks */ -+ securityId = sepgsql_opfamily_create(opcname, namespaceoid, amoid); -+ - /* - * Create it ... again no need for more permissions ... - */ - opfamilyoid = CreateOpFamily(stmt->amname, opcname, -- namespaceoid, amoid); -+ namespaceoid, amoid, securityId); - } - } - -@@ -505,6 +514,12 @@ DefineOpClass(CreateOpClassStmt *stmt) - stmt->amname))); - } - -+ /* SELinux checks */ -+ sepgsql_opfamily_alter(opfamilyoid, false, amoid, -+ operators, procedures); -+ sepgsql_opclass_create(opcname, namespaceoid, -+ typeoid, opfamilyoid, storageoid); -+ - rel = heap_open(OperatorClassRelationId, RowExclusiveLock); - - /* -@@ -650,6 +665,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) - NameData opfName; - ObjectAddress myself, - referenced; -+ Oid securityId; - - /* Convert list of names to a name and namespace */ - namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname, -@@ -701,6 +717,9 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) - errmsg("operator family \"%s\" for access method \"%s\" already exists", - opfname, stmt->amname))); - -+ /* SELinux checks */ -+ securityId = sepgsql_opfamily_create(opfname, namespaceoid, amoid); -+ - /* - * Okay, let's create the pg_opfamily entry. - */ -@@ -715,6 +734,8 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) - - tup = heap_form_tuple(rel->rd_att, values, nulls); - -+ HeapTupleSetSecid(tup, securityId); -+ - opfamilyoid = simple_heap_insert(rel, tup); - - CatalogUpdateIndexes(rel, tup); -@@ -925,6 +946,9 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid, - break; - } - } -+ /* SELinux checks */ -+ sepgsql_opfamily_alter(opfamilyoid, false, amoid, -+ operators, procedures); - - /* - * Add tuples to pg_amop and pg_amproc tying in the operators and -@@ -1002,6 +1026,9 @@ AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid, - } - } - -+ /* SELinux checks */ -+ sepgsql_opfamily_alter(opfamilyoid, true, amoid, operators, procedures); -+ - /* - * Remove tuples from pg_amop and pg_amproc. - */ -@@ -1522,6 +1549,9 @@ RemoveOpClass(RemoveOpClassStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, - NameListToString(stmt->opclassname)); - -+ /* SELinux checks */ -+ sepgsql_opclass_drop(opcID, false); -+ - ReleaseSysCache(tuple); - - /* -@@ -1583,6 +1613,9 @@ RemoveOpFamily(RemoveOpFamilyStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, - NameListToString(stmt->opfamilyname)); - -+ /* SELinux checks */ -+ sepgsql_opfamily_drop(opfID, false); -+ - ReleaseSysCache(tuple); - - /* -@@ -1781,6 +1814,9 @@ RenameOpClass(List *name, const char *access_method, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux checks */ -+ sepgsql_opclass_alter_rename(opcOid, newname); -+ - /* rename */ - namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname); - simple_heap_update(rel, &tup->t_self, tup); -@@ -1875,6 +1911,9 @@ RenameOpFamily(List *name, const char *access_method, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux checks */ -+ sepgsql_opfamily_alter_rename(opfOid, newname); -+ - /* rename */ - namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); - simple_heap_update(rel, &tup->t_self, tup); -@@ -2011,6 +2050,8 @@ AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux checks */ -+ sepgsql_opclass_alter(HeapTupleGetOid(tup)); - - /* - * Modify the owner --- okay to scribble on tup because it's a copy -@@ -2154,7 +2195,8 @@ AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -- -+ /* SELinux checks */ -+ sepgsql_opfamily_alter_owner(HeapTupleGetOid(tup), newOwnerId); - /* - * Modify the owner --- okay to scribble on tup because it's a copy - */ -diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c -index fa84a9b..36a4ac7 100644 ---- a/src/backend/commands/operatorcmds.c -+++ b/src/backend/commands/operatorcmds.c -@@ -45,6 +45,7 @@ - #include "parser/parse_func.h" - #include "parser/parse_oper.h" - #include "parser/parse_type.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/lsyscache.h" - #include "utils/rel.h" -@@ -329,6 +330,9 @@ RemoveOperator(RemoveFuncStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, - NameListToString(operatorName)); - -+ /* SELinux checks */ -+ sepgsql_operator_drop(operOid, false); -+ - ReleaseSysCache(tup); - - /* -@@ -436,6 +440,8 @@ AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(oprForm->oprnamespace)); - } -+ /* SELinux checks */ -+ sepgsql_operator_alter(operOid); - - /* - * Modify the owner --- okay to scribble on tup because it's a copy -diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c -index 7fa8278..9b3662d 100644 ---- a/src/backend/commands/prepare.c -+++ b/src/backend/commands/prepare.c -@@ -759,7 +759,7 @@ pg_prepared_statement(PG_FUNCTION_ARGS) - * build tupdesc for result tuples. This must match the definition of the - * pg_prepared_statements view in system_views.sql - */ -- tupdesc = CreateTemplateTupleDesc(5, false); -+ tupdesc = CreateTemplateTupleDesc(5, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", -diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c -index 8292ae1..0ce601a 100644 ---- a/src/backend/commands/proclang.c -+++ b/src/backend/commands/proclang.c -@@ -29,6 +29,7 @@ - #include "miscadmin.h" - #include "parser/parse_func.h" - #include "parser/parser.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -145,7 +146,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) - NIL, - PointerGetDatum(NULL), - 1, -- 0); -+ 0, -+ InvalidOid); - } - - /* -@@ -180,7 +182,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) - NIL, - PointerGetDatum(NULL), - 1, -- 0); -+ 0, -+ InvalidOid); - } - } - else -@@ -218,7 +221,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) - NIL, - PointerGetDatum(NULL), - 1, -- 0); -+ 0, -+ InvalidOid); - } - } - else -diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c -index b30fdce..8430e3e 100644 ---- a/src/backend/commands/schemacmds.c -+++ b/src/backend/commands/schemacmds.c -@@ -21,10 +21,12 @@ - #include "catalog/indexing.h" - #include "catalog/namespace.h" - #include "catalog/pg_namespace.h" -+#include "catalog/pg_seclabel.h" - #include "commands/dbcommands.h" - #include "commands/schemacmds.h" - #include "miscadmin.h" - #include "parser/parse_utilcmd.h" -+#include "sepgsql/hooks.h" - #include "tcop/utility.h" - #include "utils/acl.h" - #include "utils/builtins.h" -@@ -49,6 +51,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) - Oid owner_uid; - Oid saved_uid; - int save_sec_context; -+ Oid secid; - AclResult aclresult; - - GetUserIdAndSecContext(&saved_uid, &save_sec_context); -@@ -75,6 +78,9 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) - - check_is_member_of_role(saved_uid, owner_uid); - -+ /* SELinux checks */ -+ secid = sepgsql_schema_create(schemaName, false); -+ - /* Additional check to protect reserved schema names */ - if (!allowSystemTableMods && IsReservedName(schemaName)) - ereport(ERROR, -@@ -95,7 +101,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) - save_sec_context | SECURITY_LOCAL_USERID_CHANGE); - - /* Create the schema's namespace */ -- namespaceId = NamespaceCreate(schemaName, owner_uid); -+ namespaceId = NamespaceCreate(schemaName, owner_uid, secid); - - /* Advance cmd counter to make the namespace visible */ - CommandCounterIncrement(); -@@ -204,6 +210,9 @@ RemoveSchemas(DropStmt *drop) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, - namespaceName); - -+ /* SELinux checks */ -+ sepgsql_schema_drop(namespaceId, false); -+ - object.classId = NamespaceRelationId; - object.objectId = namespaceId; - object.objectSubId = 0; -@@ -288,6 +297,9 @@ RenameSchema(const char *oldname, const char *newname) - errmsg("unacceptable schema name \"%s\"", newname), - errdetail("The prefix \"pg_\" is reserved for system schemas."))); - -+ /* SELinux checks */ -+ sepgsql_schema_alter(HeapTupleGetOid(tup)); -+ - /* rename */ - namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname); - simple_heap_update(rel, &tup->t_self, tup); -@@ -389,6 +401,9 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_DATABASE, - get_database_name(MyDatabaseId)); - -+ /* SELinux checks */ -+ sepgsql_schema_alter(HeapTupleGetOid(tup)); -+ - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); - -@@ -423,3 +438,43 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) - } - - } -+ -+/* -+ * ALTER SCHEMA SECURITY LABEL TO -+ */ -+void -+AlterSchemaSecLabel(const char *name, char *new_label) -+{ -+ Relation rel; -+ HeapTuple tuple; -+ Oid namespaceId; -+ Oid securityId; -+ -+ /* open pg_namespace relation */ -+ rel = heap_open(NamespaceRelationId, RowExclusiveLock); -+ tuple = SearchSysCacheCopy1(NAMESPACENAME, -+ CStringGetDatum(name)); -+ if (!HeapTupleIsValid(tuple)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_SCHEMA), -+ errmsg("schema \"%s\" does not exist", name))); -+ namespaceId = HeapTupleGetOid(tuple); -+ -+ /* DAC permission check */ -+ if (!pg_namespace_ownercheck(namespaceId, GetUserId())) -+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, name); -+ -+ /* SELinux checks */ -+ securityId = sepgsql_schema_relabel(namespaceId, new_label); -+ -+ /* Update it */ -+ HeapTupleSetSecid(tuple, securityId); -+ -+ simple_heap_update(rel, &tuple->t_self, tuple); -+ -+ CatalogUpdateIndexes(rel, tuple); -+ -+ heap_freetuple(tuple); -+ -+ heap_close(rel, RowExclusiveLock); -+} -diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c -index 292a427..3c8a36f 100644 ---- a/src/backend/commands/sequence.c -+++ b/src/backend/commands/sequence.c -@@ -26,6 +26,7 @@ - #include "commands/tablecmds.h" - #include "miscadmin.h" - #include "nodes/makefuncs.h" -+#include "sepgsql/hooks.h" - #include "storage/bufmgr.h" - #include "storage/lmgr.h" - #include "storage/proc.h" -@@ -331,6 +332,9 @@ AlterSequence(AlterSeqStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - stmt->sequence->relname); - -+ /* SELinux checks */ -+ sepgsql_relation_alter(relid); -+ - /* do the work */ - AlterSequenceInternal(relid, stmt->options); - } -@@ -469,6 +473,9 @@ nextval_internal(Oid relid) - errmsg("permission denied for sequence %s", - RelationGetRelationName(seqrel)))); - -+ /* SELinux checks */ -+ sepgsql_sequence_next_value(elm->relid); -+ - /* read-only transactions may only modify temp sequences */ - if (!seqrel->rd_islocaltemp) - PreventCommandIfReadOnly("nextval()"); -@@ -668,6 +675,9 @@ currval_oid(PG_FUNCTION_ARGS) - errmsg("permission denied for sequence %s", - RelationGetRelationName(seqrel)))); - -+ /* SELinux checks */ -+ sepgsql_sequence_get_value(elm->relid); -+ - if (!elm->last_valid) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), -@@ -710,6 +720,9 @@ lastval(PG_FUNCTION_ARGS) - errmsg("permission denied for sequence %s", - RelationGetRelationName(seqrel)))); - -+ /* SELinux checks */ -+ sepgsql_sequence_get_value(last_used_seq->relid); -+ - result = last_used_seq->last; - relation_close(seqrel, NoLock); - -@@ -746,6 +759,9 @@ do_setval(Oid relid, int64 next, bool iscalled) - errmsg("permission denied for sequence %s", - RelationGetRelationName(seqrel)))); - -+ /* SELinux checks */ -+ sepgsql_sequence_set_value(elm->relid); -+ - /* read-only transactions may only modify temp sequences */ - if (!seqrel->rd_islocaltemp) - PreventCommandIfReadOnly("setval()"); -diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c -index 6a1804b..2453fcf 100644 ---- a/src/backend/commands/tablecmds.c -+++ b/src/backend/commands/tablecmds.c -@@ -32,6 +32,7 @@ - #include "catalog/pg_inherits_fn.h" - #include "catalog/pg_namespace.h" - #include "catalog/pg_opclass.h" -+#include "catalog/pg_seclabel.h" - #include "catalog/pg_tablespace.h" - #include "catalog/pg_trigger.h" - #include "catalog/pg_type.h" -@@ -62,12 +63,14 @@ - #include "parser/parser.h" - #include "rewrite/rewriteDefine.h" - #include "rewrite/rewriteHandler.h" -+#include "sepgsql/hooks.h" - #include "storage/bufmgr.h" - #include "storage/lmgr.h" - #include "storage/smgr.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -+#include "utils/guc.h" - #include "utils/inval.h" - #include "utils/lsyscache.h" - #include "utils/memutils.h" -@@ -223,7 +226,7 @@ static const struct dropmsgstrings dropmsgstringarray[] = { - - static void truncate_check_rel(Relation rel); - static List *MergeAttributes(List *schema, List *supers, bool istemp, -- List **supOids, List **supconstr, int *supOidCount); -+ List **supOids, List **supconstr, int *supOidCount, int *supSecidCount); - static bool MergeCheckConstraint(List *constraints, char *name, Node *expr); - static bool change_varattnos_walker(Node *node, const AttrNumber *newattno); - static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel); -@@ -271,10 +274,12 @@ static void ATOneLevelRecursion(List **wqueue, Relation rel, - static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, - AlterTableCmd *cmd); - static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, -- ColumnDef *colDef, bool isOid); -+ ColumnDef *colDef, bool isOid, bool isSecid); - static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid); - static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse, - AlterTableCmd *cmd); -+static void ATPrepAddSecLabel(List **wqueue, Relation rel, bool recurse, -+ AlterTableCmd *cmd); - static void ATExecDropNotNull(Relation rel, const char *colName); - static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, - const char *colName); -@@ -367,6 +372,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) - List *old_constraints; - bool localHasOids; - int parentOidCount; -+ int parentSecidCount; - List *rawDefaults; - List *cookedDefaults; - Datum reloptions; -@@ -374,6 +380,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) - AttrNumber attnum; - static char *validnsps[] = HEAP_RELOPT_NAMESPACES; - Oid ofTypeId; -+ Oid *secLabels; - - /* - * Truncate relname to appropriate length (probably a waste of time, as -@@ -477,7 +484,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) - */ - schema = MergeAttributes(schema, stmt->inhRelations, - stmt->relation->istemp, -- &inheritOids, &old_constraints, &parentOidCount); -+ &inheritOids, &old_constraints, -+ &parentOidCount, &parentSecidCount); - - /* - * Create a tuple descriptor from the relation schema. Note that this -@@ -489,6 +497,16 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) - localHasOids = interpretOidsOption(stmt->options); - descriptor->tdhasoid = (localHasOids || parentOidCount > 0); - -+ if ((relkind == RELKIND_RELATION && default_with_secids) || parentSecidCount > 0) -+ descriptor->tdhassecid = true; -+ -+ /* SELinux permission checks */ -+ secLabels = sepgsql_relation_create(relname, -+ relkind, -+ descriptor, -+ namespaceId, -+ inheritOids, -+ false); - /* - * Find columns with default values and prepare for insertion of the - * defaults. Pre-cooked (that is, inherited) defaults go into a list of -@@ -562,7 +580,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) - stmt->oncommit, - reloptions, - true, -- allowSystemTableMods); -+ allowSystemTableMods, -+ secLabels); - - StoreCatalogInheritance(relationId, inheritOids); - -@@ -771,6 +790,9 @@ RemoveRelations(DropStmt *drop) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - rel->relname); - -+ /* SELinux checks */ -+ sepgsql_relation_drop(relOid, false); -+ - if (!allowSystemTableMods && IsSystemClass(classform)) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -@@ -934,6 +956,9 @@ ExecuteTruncate(TruncateStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(seq_rel)); - -+ /* SELinux checks */ -+ sepgsql_relation_alter(seq_relid); -+ - seq_relids = lappend_oid(seq_relids, seq_relid); - - relation_close(seq_rel, NoLock); -@@ -1102,6 +1127,8 @@ truncate_check_rel(Relation rel) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(rel)); -+ /* SELinux checks */ -+ sepgsql_relation_truncate(rel); - - if (!allowSystemTableMods && IsSystemRelation(rel)) - ereport(ERROR, -@@ -1162,6 +1189,7 @@ storage_name(char c) - * 'supconstr' receives a list of constraints belonging to the parents, - * updated as necessary to be valid for the child. - * 'supOidCount' is set to the number of parents that have OID columns. -+ * 'supSecidCount' is set to the number of parents that have SID columns. - * - * Return value: - * Completed schema list. -@@ -1207,13 +1235,15 @@ storage_name(char c) - */ - static List * - MergeAttributes(List *schema, List *supers, bool istemp, -- List **supOids, List **supconstr, int *supOidCount) -+ List **supOids, List **supconstr, -+ int *supOidCount, int *supSecidCount) - { - ListCell *entry; - List *inhSchema = NIL; - List *parentOids = NIL; - List *constraints = NIL; - int parentsWithOids = 0; -+ int parentsWithSecids = 0; - bool have_bogus_defaults = false; - int child_attno; - static Node bogus_marker = {0}; /* marks conflicting defaults */ -@@ -1341,6 +1371,8 @@ MergeAttributes(List *schema, List *supers, bool istemp, - - if (relation->rd_rel->relhasoids) - parentsWithOids++; -+ if (relation->rd_rel->relhassecids) -+ parentsWithSecids++; - - tupleDesc = RelationGetDescr(relation); - constr = tupleDesc->constr; -@@ -1642,6 +1674,7 @@ MergeAttributes(List *schema, List *supers, bool istemp, - *supOids = parentOids; - *supconstr = constraints; - *supOidCount = parentsWithOids; -+ *supSecidCount = parentsWithSecids; - return schema; - } - -@@ -2001,6 +2034,9 @@ renameatt(Oid myrelid, - errmsg("permission denied: \"%s\" is a system catalog", - RelationGetRelationName(targetrelation)))); - -+ /* SELinux checks */ -+ sepgsql_attribute_alter(myrelid, oldattname); -+ - /* - * if the 'recurse' flag is set then we are supposed to rename this - * attribute in all classes that inherit from 'relname' (as well as in -@@ -2152,6 +2188,9 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype) - errmsg("\"%s\" is not a view", - RelationGetRelationName(targetrelation)))); - -+ /* SELinux checks */ -+ sepgsql_relation_alter_rename(myrelid, newrelname); -+ - /* - * Don't allow ALTER TABLE on composite types. We want people to use ALTER - * TYPE for that. -@@ -2580,6 +2619,27 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, - } - pass = AT_PASS_DROP; - break; -+ case AT_AddSecLabel: -+ ATSimplePermissions(rel, false); -+ /* Performs own recursion */ -+ if (!rel->rd_rel->relhassecids || recursing) -+ ATPrepAddSecLabel(wqueue, rel, recurse, cmd); -+ pass = AT_PASS_ADD_COL; -+ break; -+ case AT_DropSecLabel: -+ ATSimplePermissions(rel, false); -+ /* Performs own recursion */ -+ if (rel->rd_rel->relhassecids) -+ { -+ AlterTableCmd *dropCmd = makeNode(AlterTableCmd); -+ -+ dropCmd->subtype = AT_DropColumn; -+ dropCmd->name = pstrdup("security_label"); -+ dropCmd->behavior = cmd->behavior; -+ ATPrepCmd(wqueue, rel, dropCmd, recurse, false); -+ } -+ pass = AT_PASS_DROP; -+ break; - case AT_SetTableSpace: /* SET TABLESPACE */ - ATSimplePermissionsRelationOrIndex(rel); - /* This command never recurses */ -@@ -2709,7 +2769,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, - case AT_AddColumn: /* ADD COLUMN */ - case AT_AddColumnToView: /* add column via CREATE OR REPLACE - * VIEW */ -- ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false); -+ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false, false); - break; - case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */ - ATExecColumnDefault(rel, cmd->name, cmd->def); -@@ -2781,17 +2841,22 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, - case AT_AddOids: /* SET WITH OIDS */ - /* Use the ADD COLUMN code, unless prep decided to do nothing */ - if (cmd->def != NULL) -- ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true); -+ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true, false); -+ break; -+ case AT_AddSecLabel: /* SET WITH SECURITY LABEL */ -+ /* Use the ADD COLUMN code, unless prep decided to do nothing */ -+ if (cmd->def != NULL) -+ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false, true); - break; - case AT_DropOids: /* SET WITHOUT OIDS */ -- -+ case AT_DropSecLabel: /* SET WITHOUT SECURITY LABEL */ - /* - * Nothing to do here; we'll have generated a DropColumn - * subcommand to do the real work - */ - break; - case AT_SetTableSpace: /* SET TABLESPACE */ -- -+ sepgsql_relation_alter(RelationGetRelid(rel)); - /* - * Nothing to do here; Phase 3 does the work - */ -@@ -3146,6 +3211,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) - MemoryContext oldCxt; - List *dropped_attrs = NIL; - ListCell *lc; -+ Oid tupSecidInherit = InvalidOid; - - econtext = GetPerTupleExprContext(estate); - -@@ -3176,6 +3242,19 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) - } - - /* -+ * If this routine is called due to ALTER TABLE SET WITH SECURITY LABEL, -+ * security label of the relation shall be assigned tuples. -+ */ -+ if (!oldTupDesc->tdhassecid && newTupDesc->tdhassecid) -+ { -+ Oid relSecid = GetSysCacheSecid1(RELOID, -+ ObjectIdGetDatum(tab->relid)); -+ -+ tupSecidInherit = seclabelMoveSecid(RelationGetRelid(oldrel), -+ RelationRelationId, relSecid); -+ } -+ -+ /* - * Scan through the rows, generating a new row if needed and then - * checking all the constraints. - */ -@@ -3192,11 +3271,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) - if (newrel) - { - Oid tupOid = InvalidOid; -+ Oid tupSecid = InvalidOid; - - /* Extract data from old tuple */ - heap_deform_tuple(tuple, oldTupDesc, values, isnull); - if (oldTupDesc->tdhasoid) - tupOid = HeapTupleGetOid(tuple); -+ if (oldTupDesc->tdhassecid) -+ tupSecid = HeapTupleGetSecid(tuple); -+ else -+ tupSecid = tupSecidInherit; - - /* Set dropped attributes to null in new tuple */ - foreach(lc, dropped_attrs) -@@ -3228,6 +3312,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) - /* Preserve OID, if any */ - if (newTupDesc->tdhasoid) - HeapTupleSetOid(tuple, tupOid); -+ /* Preserve security-id, if any */ -+ if (newTupDesc->tdhassecid) -+ HeapTupleSetSecid(tuple, tupSecid); - } - - /* Now check any constraints on the possibly-changed tuple */ -@@ -3626,7 +3713,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, - - static void - ATExecAddColumn(AlteredTableInfo *tab, Relation rel, -- ColumnDef *colDef, bool isOid) -+ ColumnDef *colDef, bool isOid, bool isSecid) - { - Oid myrelid = RelationGetRelid(rel); - Relation pgclass, -@@ -3640,6 +3727,10 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - int32 typmod; - Form_pg_type tform; - Expr *defval; -+ Oid securityId; -+ -+ /* SELinux permission check */ -+ securityId = sepgsql_attribute_create(myrelid, colDef->colname); - - attrdesc = heap_open(AttributeRelationId, RowExclusiveLock); - -@@ -3668,6 +3759,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - errmsg("child table \"%s\" has different type for column \"%s\"", - RelationGetRelationName(rel), colDef->colname))); - -+ if (!seclabelCompareSecid(AttributeRelationId, securityId, -+ AttributeRelationId, HeapTupleGetSecid(tuple))) -+ ereport(ERROR, -+ (errcode(ERRCODE_DATATYPE_MISMATCH), -+ errmsg("child table \"%s\" has different label for column \"%s\"", -+ RelationGetRelationName(rel), colDef->colname))); -+ - /* If it's OID, child column must actually be OID */ - if (isOid && childatt->attnum != ObjectIdAttributeNumber) - ereport(ERROR, -@@ -3675,6 +3773,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - errmsg("child table \"%s\" has a conflicting \"%s\" column", - RelationGetRelationName(rel), colDef->colname))); - -+ /* If it's SecID, child column must actually be SecID */ -+ if (isSecid && childatt->attnum != SecurityLabelAttributeNumber) -+ ereport(ERROR, -+ (errcode(ERRCODE_DATATYPE_MISMATCH), -+ errmsg("child table \"%s\" has a conflicting \"%s\" column", -+ RelationGetRelationName(rel), colDef->colname))); -+ - /* Bump the existing child att's inhcount */ - childatt->attinhcount++; - simple_heap_update(attrdesc, &tuple->t_self, tuple); -@@ -3714,6 +3819,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - /* Determine the new attribute's number */ - if (isOid) - newattnum = ObjectIdAttributeNumber; -+ else if (isSecid) -+ newattnum = SecurityLabelAttributeNumber; - else - { - newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1; -@@ -3753,7 +3860,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - - ReleaseSysCache(typeTuple); - -- InsertPgAttributeTuple(attrdesc, &attribute, NULL); -+ InsertPgAttributeTuple(attrdesc, &attribute, NULL, securityId); - - heap_close(attrdesc, RowExclusiveLock); - -@@ -3762,6 +3869,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - */ - if (isOid) - ((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true; -+ else if (isSecid) -+ ((Form_pg_class) GETSTRUCT(reltup))->relhassecids = true; - else - ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum; - -@@ -3873,7 +3982,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - * If we are adding an OID column, we have to tell Phase 3 to rewrite the - * table to fix that. - */ -- if (isOid) -+ if (isOid || isSecid) - tab->new_changeoids = true; - - /* -@@ -3926,6 +4035,31 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) - } - - /* -+ * ALTER TABLE SET WITH SECURITY LABEL -+ * -+ * Basically this is an ADD COLUMN for the special SecLabel column. -+ * We have to cons up a ColumnDef node because the ADD COLUMN code needs one. -+ */ -+static void -+ATPrepAddSecLabel(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) -+{ -+ /* If we're recursing to a child table, the ColumnDef is already set up */ -+ if (cmd->def == NULL) -+ { -+ ColumnDef *cdef = makeNode(ColumnDef); -+ -+ cdef->colname = pstrdup("security_label"); -+ cdef->typeName = makeTypeNameFromOid(TEXTOID, -1); -+ cdef->inhcount = 0; -+ cdef->is_local = true; -+ cdef->is_not_null = true; -+ cdef->storage = 0; -+ cmd->def = (Node *) cdef; -+ } -+ ATPrepAddColumn(wqueue, rel, recurse, cmd); -+} -+ -+/* - * ALTER TABLE ALTER COLUMN DROP NOT NULL - */ - static void -@@ -3937,6 +4071,9 @@ ATExecDropNotNull(Relation rel, const char *colName) - List *indexoidlist; - ListCell *indexoidscan; - -+ /* SELinux checks */ -+ sepgsql_attribute_alter(RelationGetRelid(rel), colName); -+ - /* - * lookup the attribute - */ -@@ -4027,6 +4164,9 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, - AttrNumber attnum; - Relation attr_rel; - -+ /* SELinux checks */ -+ sepgsql_attribute_alter(RelationGetRelid(rel), colName); -+ - /* - * lookup the attribute - */ -@@ -4077,6 +4217,9 @@ ATExecColumnDefault(Relation rel, const char *colName, - { - AttrNumber attnum; - -+ /* SELinux checks */ -+ sepgsql_attribute_alter(RelationGetRelid(rel), colName); -+ - /* - * get the number of the attribute - */ -@@ -4151,6 +4294,9 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue) - HeapTuple tuple; - Form_pg_attribute attrtuple; - -+ /* SELinux checks */ -+ sepgsql_attribute_alter(RelationGetRelid(rel), colName); -+ - Assert(IsA(newValue, Integer)); - newtarget = intVal(newValue); - -@@ -4217,6 +4363,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, - bool repl_null[Natts_pg_attribute]; - bool repl_repl[Natts_pg_attribute]; - -+ /* SELinux checks */ -+ sepgsql_attribute_alter(RelationGetRelid(rel), colName); -+ - attrelation = heap_open(AttributeRelationId, RowExclusiveLock); - - tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName); -@@ -4276,6 +4425,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue) - HeapTuple tuple; - Form_pg_attribute attrtuple; - -+ /* SELinux checks */ -+ sepgsql_attribute_alter(RelationGetRelid(rel), colName); -+ - Assert(IsA(newValue, String)); - storagemode = strVal(newValue); - -@@ -4374,6 +4526,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, - if (recursing) - ATSimplePermissions(rel, false); - -+ /* SELinux checks */ -+ sepgsql_attribute_drop(RelationGetRelid(rel), colName, false); -+ - /* - * get the number of the attribute - */ -@@ -4399,8 +4554,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, - - attnum = targetatt->attnum; - -- /* Can't drop a system attribute, except OID */ -- if (attnum <= 0 && attnum != ObjectIdAttributeNumber) -+ /* Can't drop a system attribute, except OID/SecID */ -+ if (attnum <= 0 && -+ attnum != ObjectIdAttributeNumber && -+ attnum != SecurityLabelAttributeNumber) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot drop system column \"%s\"", -@@ -4516,7 +4673,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, - * If we dropped the OID column, must adjust pg_class.relhasoids and tell - * Phase 3 to physically get rid of the column. - */ -- if (attnum == ObjectIdAttributeNumber) -+ if (attnum == ObjectIdAttributeNumber || -+ attnum == SecurityLabelAttributeNumber) - { - Relation class_rel; - Form_pg_class tuple_class; -@@ -4531,7 +4689,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, - RelationGetRelid(rel)); - tuple_class = (Form_pg_class) GETSTRUCT(tuple); - -- tuple_class->relhasoids = false; -+ if (attnum == ObjectIdAttributeNumber) -+ tuple_class->relhasoids = false; -+ if (attnum == SecurityLabelAttributeNumber) -+ tuple_class->relhassecids = false; -+ - simple_heap_update(class_rel, &tuple->t_self, tuple); - - /* Keep the catalog indexes up to date */ -@@ -4678,6 +4840,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, - if (recursing) - ATSimplePermissions(rel, false); - -+ /* SELinux checks */ -+ sepgsql_relation_alter(RelationGetRelid(rel)); -+ - /* - * Call AddRelationNewConstraints to do the work, making sure it works on - * a copy of the Constraint so transformExpr can't modify the original. It -@@ -4884,6 +5049,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, - checkFkeyPermissions(pkrel, pkattnum, numpks); - checkFkeyPermissions(rel, fkattnum, numfks); - -+ /* SELinux checks */ -+ sepgsql_relation_alter(RelationGetRelid(rel)); -+ - /* - * Look up the equality operators to use in the constraint. - * -@@ -5629,6 +5797,9 @@ ATExecDropConstraint(Relation rel, const char *constrName, - if (recursing) - ATSimplePermissions(rel, false); - -+ /* SELinux checks */ -+ sepgsql_relation_alter(RelationGetRelid(rel)); -+ - conrel = heap_open(ConstraintRelationId, RowExclusiveLock); - - /* -@@ -5966,6 +6137,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, - SysScanDesc scan; - HeapTuple depTup; - -+ /* SELinux checks */ -+ sepgsql_attribute_alter(RelationGetRelid(rel), colName); -+ - attrelation = heap_open(AttributeRelationId, RowExclusiveLock); - - /* Look up the target column */ -@@ -6596,6 +6770,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux checks */ -+ sepgsql_relation_alter(relationOid); - } - - memset(repl_null, false, sizeof(repl_null)); -@@ -6761,6 +6937,9 @@ ATExecClusterOn(Relation rel, const char *indexName) - { - Oid indexOid; - -+ /* SELinux checks */ -+ sepgsql_relation_alter(RelationGetRelid(rel)); -+ - indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace); - - if (!OidIsValid(indexOid)) -@@ -6785,6 +6964,9 @@ ATExecClusterOn(Relation rel, const char *indexName) - static void - ATExecDropCluster(Relation rel) - { -+ /* SELinux checks */ -+ sepgsql_relation_alter(RelationGetRelid(rel)); -+ - mark_index_clustered(rel, InvalidOid); - } - -@@ -6835,6 +7017,9 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) - bool repl_repl[Natts_pg_class]; - static char *validnsps[] = HEAP_RELOPT_NAMESPACES; - -+ /* SELinux checks */ -+ sepgsql_relation_alter(RelationGetRelid(rel)); -+ - if (defList == NIL) - return; /* nothing to do */ - -@@ -7163,6 +7348,9 @@ static void - ATExecEnableDisableTrigger(Relation rel, char *trigname, - char fires_when, bool skip_system) - { -+ /* SELinux checks */ -+ sepgsql_relation_alter(RelationGetRelid(rel)); -+ - EnableDisableTrigger(rel, trigname, fires_when, skip_system); - } - -@@ -7175,6 +7363,9 @@ static void - ATExecEnableDisableRule(Relation rel, char *trigname, - char fires_when) - { -+ /* SELinux checks */ -+ sepgsql_relation_alter(RelationGetRelid(rel)); -+ - EnableDisableRule(rel, trigname, fires_when); - } - -@@ -7217,6 +7408,10 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent) - */ - ATSimplePermissions(parent_rel, false); - -+ /* SELinux checks */ -+ sepgsql_relation_alter_inherit(RelationGetRelid(child_rel), -+ RelationGetRelid(parent_rel)); -+ - /* Permanent rels cannot inherit from temporary ones */ - if (parent_rel->rd_istemp && !child_rel->rd_istemp) - ereport(ERROR, -@@ -7569,6 +7764,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent) - List *connames; - bool found = false; - -+ /* SELinux checks */ -+ sepgsql_relation_alter(RelationGetRelid(rel)); -+ - /* - * AccessShareLock on the parent is probably enough, seeing that DROP - * TABLE doesn't lock parent tables at all. We need some lock since we'll -@@ -7866,6 +8064,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, - /* get schema OID and check its permissions */ - nspOid = LookupCreationNamespace(newschema); - -+ /* SELinux checks */ -+ sepgsql_relation_alter_schema(RelationGetRelid(rel), nspOid); -+ - if (oldNspOid == nspOid) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_TABLE), -@@ -8063,6 +8264,282 @@ AlterSeqNamespaces(Relation classRel, Relation rel, - relation_close(depRel, AccessShareLock); - } - -+/* -+ * ALTER TABLE/SEQUENCE/VIEW SECURITY LABEL TO -+ */ -+void -+AlterRelationSecLabelInternal(Oid relOid, Oid securityId, int expected_parents) -+{ -+ Form_pg_class classForm; -+ Relation targetRel; -+ Relation inhRel; -+ Relation classRel; -+ SysScanDesc scan; -+ ScanKeyData skey; -+ HeapTuple tuple; -+ List *indexList; -+ ListCell *cell; -+ int inhcount = 0; -+ -+ /* -+ * Grab an exclusive lock on the target table -+ */ -+ targetRel = relation_open(relOid, AccessExclusiveLock); -+ -+ /* -+ * Check num of inheritors -+ */ -+ inhRel = heap_open(InheritsRelationId, AccessShareLock); -+ -+ ScanKeyInit(&skey, -+ Anum_pg_inherits_inhrelid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(relOid)); -+ -+ scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, -+ true, SnapshotNow, 1, &skey); -+ while (HeapTupleIsValid(systable_getnext(scan))) -+ inhcount++; -+ -+ systable_endscan(scan); -+ -+ heap_close(inhRel, AccessShareLock); -+ -+ if (inhcount != expected_parents) -+ ereport(ERROR, -+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION), -+ errmsg("cannot relabel inherited relation"))); -+ -+ /* -+ * Update pg_class relation -+ */ -+ classRel = heap_open(RelationRelationId, RowExclusiveLock); -+ -+ tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation %u", relOid); -+ -+ classForm = (Form_pg_class) GETSTRUCT(tuple); -+ -+ HeapTupleSetSecid(tuple, securityId); -+ -+ simple_heap_update(classRel, &tuple->t_self, tuple); -+ -+ CatalogUpdateIndexes(classRel, tuple); -+ -+ heap_close(classRel, RowExclusiveLock); -+ -+ /* -+ * Also update TOAST and INDEX -+ */ -+ if (OidIsValid(classForm->reltoastrelid)) -+ AlterRelationSecLabelInternal(classForm->reltoastrelid, -+ securityId, 0); -+ -+ indexList = RelationGetIndexList(targetRel); -+ foreach (cell, indexList) -+ AlterRelationSecLabelInternal(lfirst_oid(cell), securityId, 0); -+ -+ /* -+ * Also update pg_attribute, if not RELKIND_RELATION -+ */ -+ if (classForm->relkind != RELKIND_RELATION) -+ { -+ Relation attRel; -+ HeapTuple oldtup, newtup; -+ Oid attsecid; -+ -+ /* move security-id to pg_attribtue catalog */ -+ attsecid = seclabelMoveSecid(AttributeRelationId, -+ RelationRelationId, -+ securityId); -+ -+ attRel = heap_open(AttributeRelationId, RowExclusiveLock); -+ -+ ScanKeyInit(&skey, -+ Anum_pg_attribute_attrelid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(RelationGetRelid(targetRel))); -+ -+ scan = systable_beginscan(attRel, AttributeRelidNumIndexId, true, -+ SnapshotNow, 1, &skey); -+ while (HeapTupleIsValid(oldtup = systable_getnext(scan))) -+ { -+ Form_pg_attribute attForm -+ = (Form_pg_attribute) GETSTRUCT(oldtup); -+ -+ if (attForm->attinhcount > 0) -+ elog(ERROR, "Bug? attinhcount is %d at %s of %s", -+ attForm->attinhcount, NameStr(attForm->attname), -+ RelationGetRelationName(targetRel)); -+ -+ newtup = heap_copytuple(oldtup); -+ -+ HeapTupleSetSecid(newtup, attsecid); -+ -+ simple_heap_update(attRel, &newtup->t_self, newtup); -+ -+ CatalogUpdateIndexes(attRel, newtup); -+ } -+ systable_endscan(scan); -+ -+ heap_close(attRel, RowExclusiveLock); -+ } -+ -+ heap_close(targetRel, NoLock); /* close rel but keep lock */ -+} -+ -+void -+AlterAttributeSecLabelInternal(Oid relOid, const char *attname, -+ Oid securityId, int expected_parents) -+{ -+ Form_pg_attribute attForm; -+ Relation targetRel; -+ Relation attRel; -+ HeapTuple tuple; -+ -+ /* -+ * Grab an exclusive lock on the target table, which we will NOT -+ * release until end of transaction. -+ */ -+ targetRel = heap_open(relOid, AccessExclusiveLock); -+ -+ attRel = heap_open(AttributeRelationId, RowExclusiveLock); -+ -+ tuple = SearchSysCacheCopyAttName(relOid, attname); -+ if (!HeapTupleIsValid(tuple)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_COLUMN), -+ errmsg("column \"%s\" does not exist", attname))); -+ -+ attForm = (Form_pg_attribute) GETSTRUCT(tuple); -+#if 0 -+ /* -+ * XXX - here is no active reason why we forbid to relabel -+ * system columns. -+ */ -+ if (attForm->attnum <= 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("cannot relabel system column \"%s\"", attname))); -+#endif -+ if (attForm->attinhcount > expected_parents) -+ ereport(ERROR, -+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION), -+ errmsg("cannot relabel inherited column \"%s\"", attname))); -+ -+ /* update pg_attribute */ -+ HeapTupleSetSecid(tuple, securityId); -+ -+ simple_heap_update(attRel, &tuple->t_self, tuple); -+ -+ CatalogUpdateIndexes(attRel, tuple); -+ -+ heap_close(attRel, RowExclusiveLock); -+ -+ heap_close(targetRel, NoLock); /* close rel but keep lock */ -+} -+ -+void -+AlterRelationSecLabel(RangeVar *relation, const char *attname, -+ ObjectType objtype, char *new_label) -+{ -+ Oid relOid = RangeVarGetRelid(relation, false); -+ Oid securityId; -+ char relkind; -+ List *child_oids, *child_numparents; -+ ListCell *lo, *li; -+ -+ /* -+ * Sanity checks for relation types -+ */ -+ relkind = get_rel_relkind(relOid); -+ switch (objtype) -+ { -+ case OBJECT_TABLE: -+ Assert(attname == NULL); -+ if (relkind != RELKIND_RELATION && -+ relkind != RELKIND_SEQUENCE && -+ relkind != RELKIND_VIEW) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not a table, sequence or view", -+ get_rel_name(relOid)))); -+ break; -+ -+ case OBJECT_SEQUENCE: -+ Assert(attname == NULL); -+ if (relkind != RELKIND_SEQUENCE) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not a sequence", -+ get_rel_name(relOid)))); -+ break; -+ -+ case OBJECT_VIEW: -+ Assert(attname == NULL); -+ if (relkind != RELKIND_VIEW) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not a view", -+ get_rel_name(relOid)))); -+ break; -+ -+ case OBJECT_COLUMN: -+ Assert(attname != NULL); -+ if (relkind != RELKIND_RELATION) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not a table", -+ get_rel_name(relOid)))); -+ break; -+ -+ default: -+ elog(ERROR, "Bug? unexpected object type %d", objtype); -+ break; -+ } -+ -+ /* -+ * Recursive calls to child relations including myself -+ */ -+ child_oids = find_all_inheritors(relOid, -+ AccessExclusiveLock, -+ &child_numparents); -+ forboth (lo, child_oids, li, child_numparents) -+ { -+ Oid childOid = lfirst_oid(lo); -+ int numParents = lfirst_int(li); -+ Oid relnsp = get_rel_namespace(childOid); -+ -+ /* Permission checks */ -+ if (!pg_class_ownercheck(childOid, GetUserId())) -+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, -+ get_rel_name(childOid)); -+ -+ if (!allowSystemTableMods && -+ (IsSystemNamespace(relnsp) || IsToastNamespace(relnsp))) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("permission denied: \"%s\" is a system catalog", -+ get_rel_name(childOid)))); -+ -+ /* SELinux checks */ -+ if (objtype != OBJECT_COLUMN) -+ securityId = sepgsql_relation_relabel(childOid, -+ new_label); -+ else -+ securityId = sepgsql_attribute_relabel(childOid, attname, -+ new_label); -+ -+ /* Do work */ -+ if (objtype != OBJECT_COLUMN) -+ AlterRelationSecLabelInternal(childOid, -+ securityId, numParents); -+ else -+ AlterAttributeSecLabelInternal(childOid, attname, -+ securityId, numParents); -+ } -+} - - /* - * This code supports -diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c -index 4c57a73..ce0bb99 100644 ---- a/src/backend/commands/tablespace.c -+++ b/src/backend/commands/tablespace.c -@@ -65,6 +65,7 @@ - #include "commands/tablespace.h" - #include "miscadmin.h" - #include "postmaster/bgwriter.h" -+#include "sepgsql/hooks.h" - #include "storage/fd.h" - #include "storage/procarray.h" - #include "storage/standby.h" -@@ -235,6 +236,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) - Oid tablespaceoid; - char *location; - Oid ownerId; -+ Oid securityId; - - /* Must be super user */ - if (!superuser()) -@@ -244,6 +246,9 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) - stmt->tablespacename), - errhint("Must be superuser to create a tablespace."))); - -+ /* SELinux check */ -+ securityId = sepgsql_tablespace_create(stmt->tablespacename); -+ - /* However, the eventual owner of the tablespace need not be */ - if (stmt->owner) - ownerId = get_roleid_checked(stmt->owner); -@@ -324,6 +329,8 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) - - tuple = heap_form_tuple(rel->rd_att, values, nulls); - -+ HeapTupleSetSecid(tuple, securityId); -+ - tablespaceoid = simple_heap_insert(rel, tuple); - - CatalogUpdateIndexes(rel, tuple); -@@ -429,6 +436,9 @@ DropTableSpace(DropTableSpaceStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, - tablespacename); - -+ /* SELinux checks */ -+ sepgsql_tablespace_drop(tablespaceoid, false); -+ - /* Disallow drop of the standard tablespaces, even by superuser */ - if (tablespaceoid == GLOBALTABLESPACE_OID || - tablespaceoid == DEFAULTTABLESPACE_OID) -@@ -817,6 +827,9 @@ RenameTableSpace(const char *oldname, const char *newname) - if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId())) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, oldname); - -+ /* SELinux checks */ -+ sepgsql_tablespace_alter(HeapTupleGetOid(newtuple)); -+ - /* Validate new name */ - if (!allowSystemTableMods && IsReservedName(newname)) - ereport(ERROR, -@@ -898,6 +911,9 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId) - /* Must be able to become new owner */ - check_is_member_of_role(GetUserId(), newOwnerId); - -+ /* SELinux checks */ -+ sepgsql_tablespace_alter(HeapTupleGetOid(tup)); -+ - /* - * Normally we would also check for create permissions here, but there - * are none for tablespaces so we follow what rename tablespace does -@@ -1015,6 +1031,54 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt) - } - - /* -+ * ALTER TABLESPACE xxx SECURITY LABEL TO ... -+ */ -+void -+AlterTableSpaceSecLabel(const char *tspaceName, char *newLabel) -+{ -+ Relation rel; -+ ScanKeyData skey; -+ HeapScanDesc scan; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ Oid securityId; -+ -+ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); -+ -+ /* scan pg_tablespace catalog */ -+ ScanKeyInit(&skey, -+ Anum_pg_tablespace_spcname, -+ BTEqualStrategyNumber, F_NAMEEQ, -+ CStringGetDatum(tspaceName)); -+ scan = heap_beginscan(rel, SnapshotNow, 1, &skey); -+ oldtup = heap_getnext(scan, ForwardScanDirection); -+ if (!HeapTupleIsValid(oldtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_OBJECT), -+ errmsg("tablespace \"%s\" does not exist", tspaceName))); -+ -+ /* Must be owner */ -+ if (!pg_tablespace_ownercheck(HeapTupleGetOid(oldtup), GetUserId())) -+ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, tspaceName); -+ -+ /* SELinux checks */ -+ securityId = sepgsql_tablespace_relabel(HeapTupleGetSecid(oldtup), newLabel); -+ -+ /* update it */ -+ newtup = heap_copytuple(oldtup); -+ -+ HeapTupleSetSecid(newtup, securityId); -+ -+ simple_heap_update(rel, &newtup->t_self, newtup); -+ -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_endscan(scan); -+ -+ heap_close(rel, RowExclusiveLock); -+} -+ -+/* - * Routines for handling the GUC variable 'default_tablespace'. - */ - -diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c -index 251c3e8..2f7d3d5 100644 ---- a/src/backend/commands/trigger.c -+++ b/src/backend/commands/trigger.c -@@ -40,6 +40,7 @@ - #include "parser/parsetree.h" - #include "pgstat.h" - #include "rewrite/rewriteManip.h" -+#include "sepgsql/hooks.h" - #include "storage/bufmgr.h" - #include "tcop/utility.h" - #include "utils/acl.h" -@@ -330,6 +331,10 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, - NameListToString(stmt->funcname)))); - } - -+ /* SELinux checks */ -+ sepgsql_trigger_create(RelationGetRelid(rel), stmt->trigname, -+ constrrelid, funcoid); -+ - /* - * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that - * references one of the built-in RI_FKey trigger functions, assume it is -@@ -1007,6 +1012,9 @@ DropTrigger(Oid relid, const char *trigname, DropBehavior behavior, - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - get_rel_name(relid)); - -+ /* SELinux checks */ -+ sepgsql_trigger_drop(relid, trigname, false); -+ - object.classId = TriggerRelationId; - object.objectId = HeapTupleGetOid(tup); - object.objectSubId = 0; -@@ -1116,6 +1124,9 @@ renametrig(Oid relid, - SysScanDesc tgscan; - ScanKeyData key[2]; - -+ /* SELinux checks */ -+ sepgsql_trigger_alter(relid, oldname); -+ - /* - * Grab an exclusive lock on the target table, which we will NOT release - * until end of transaction. -diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c -index 522ea6e..8c6e0d3 100644 ---- a/src/backend/commands/tsearchcmds.c -+++ b/src/backend/commands/tsearchcmds.c -@@ -35,6 +35,7 @@ - #include "miscadmin.h" - #include "nodes/makefuncs.h" - #include "parser/parse_func.h" -+#include "sepgsql/hooks.h" - #include "tsearch/ts_cache.h" - #include "tsearch/ts_public.h" - #include "tsearch/ts_utils.h" -@@ -171,6 +172,7 @@ DefineTSParser(List *names, List *parameters) - NameData pname; - Oid prsOid; - Oid namespaceoid; -+ Oid securityId; - - if (!superuser()) - ereport(ERROR, -@@ -250,6 +252,14 @@ DefineTSParser(List *names, List *parameters) - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("text search parser lextypes method is required"))); - -+ /* SELinux checks */ -+ securityId = sepgsql_ts_parser_create(prsname, namespaceoid, -+ DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_parser_prsheadline - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])); -+ - /* - * Looks good, insert - */ -@@ -257,6 +267,8 @@ DefineTSParser(List *names, List *parameters) - - tup = heap_form_tuple(prsRel->rd_att, values, nulls); - -+ HeapTupleSetSecid(tup, securityId); -+ - prsOid = simple_heap_insert(prsRel, tup); - - CatalogUpdateIndexes(prsRel, tup); -@@ -314,6 +326,8 @@ RemoveTSParsers(DropStmt *drop) - } - continue; - } -+ /* SELinux checks */ -+ sepgsql_ts_parser_drop(prsOid, false); - - object.classId = TSParserRelationId; - object.objectId = prsOid; -@@ -366,10 +380,13 @@ RenameTSParser(List *oldname, const char *newname) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to rename text search parsers"))); - -- rel = heap_open(TSParserRelationId, RowExclusiveLock); -- - prsId = TSParserGetPrsid(oldname, false); - -+ /* SELinux checks */ -+ sepgsql_ts_parser_alter_rename(prsId, newname); -+ -+ rel = heap_open(TSParserRelationId, RowExclusiveLock); -+ - tup = SearchSysCacheCopy1(TSPARSEROID, ObjectIdGetDatum(prsId)); - - if (!HeapTupleIsValid(tup)) /* should not happen */ -@@ -496,6 +513,7 @@ DefineTSDictionary(List *names, List *parameters) - List *dictoptions = NIL; - Oid dictOid; - Oid namespaceoid; -+ Oid securityId; - AclResult aclresult; - char *dictname; - -@@ -508,6 +526,9 @@ DefineTSDictionary(List *names, List *parameters) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceoid)); - -+ /* SELinux checks */ -+ securityId = sepgsql_ts_dict_create(dictname, namespaceoid); -+ - /* - * loop over the definition list and extract the information we need. - */ -@@ -557,6 +578,8 @@ DefineTSDictionary(List *names, List *parameters) - - tup = heap_form_tuple(dictRel->rd_att, values, nulls); - -+ HeapTupleSetSecid(tup, securityId); -+ - dictOid = simple_heap_insert(dictRel, tup); - - CatalogUpdateIndexes(dictRel, tup); -@@ -610,6 +633,8 @@ RenameTSDictionary(List *oldname, const char *newname) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); -+ /* SELinux checks */ -+ sepgsql_ts_dict_alter_rename(dictId, newname); - - namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname); - simple_heap_update(rel, &tup->t_self, tup); -@@ -674,6 +699,8 @@ RemoveTSDictionaries(DropStmt *drop) - !pg_namespace_ownercheck(namespaceId, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, - NameListToString(names)); -+ /* SELinux checks */ -+ sepgsql_ts_dict_drop(dictOid, false); - - object.classId = TSDictionaryRelationId; - object.objectId = dictOid; -@@ -745,6 +772,8 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt) - if (!pg_ts_dict_ownercheck(dictId, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, - NameListToString(stmt->dictname)); -+ /* SELinux checks */ -+ sepgsql_ts_dict_alter(dictId); - - /* deserialize the existing set of options */ - opt = SysCacheGetAttr(TSDICTOID, tup, -@@ -871,6 +900,8 @@ AlterTSDictionaryOwner(List *name, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux checks */ -+ sepgsql_ts_dict_alter(dictId); - - form->dictowner = newOwnerId; - -@@ -982,6 +1013,7 @@ DefineTSTemplate(List *names, List *parameters) - int i; - Oid dictOid; - Oid namespaceoid; -+ Oid securityId; - char *tmplname; - - if (!superuser()) -@@ -1036,6 +1068,10 @@ DefineTSTemplate(List *names, List *parameters) - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("text search template lexize method is required"))); - -+ /* SELinux checks */ -+ securityId = sepgsql_ts_template_create(tmplname, namespaceoid, -+ DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])); - /* - * Looks good, insert - */ -@@ -1044,6 +1080,8 @@ DefineTSTemplate(List *names, List *parameters) - - tup = heap_form_tuple(tmplRel->rd_att, values, nulls); - -+ HeapTupleSetSecid(tup, securityId); -+ - dictOid = simple_heap_insert(tmplRel, tup); - - CatalogUpdateIndexes(tmplRel, tup); -@@ -1075,6 +1113,9 @@ RenameTSTemplate(List *oldname, const char *newname) - - tmplId = TSTemplateGetTmplid(oldname, false); - -+ /* SELinux checks */ -+ sepgsql_ts_template_alter_rename(tmplId, newname); -+ - tup = SearchSysCacheCopy1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId)); - - if (!HeapTupleIsValid(tup)) /* should not happen */ -@@ -1145,6 +1186,8 @@ RemoveTSTemplates(DropStmt *drop) - } - continue; - } -+ /* SELinux checks */ -+ sepgsql_ts_template_drop(tmplOid, false); - - object.classId = TSTemplateRelationId; - object.objectId = tmplOid; -@@ -1305,6 +1348,7 @@ DefineTSConfiguration(List *names, List *parameters) - bool nulls[Natts_pg_ts_config]; - AclResult aclresult; - Oid namespaceoid; -+ Oid securityId; - char *cfgname; - NameData cname; - Oid sourceOid = InvalidOid; -@@ -1321,6 +1365,9 @@ DefineTSConfiguration(List *names, List *parameters) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceoid)); - -+ /* SELinux checks */ -+ securityId = sepgsql_ts_config_create(cfgname, namespaceoid); -+ - /* - * loop over the definition list and extract the information we need. - */ -@@ -1388,6 +1435,8 @@ DefineTSConfiguration(List *names, List *parameters) - - tup = heap_form_tuple(cfgRel->rd_att, values, nulls); - -+ HeapTupleSetSecid(tup, securityId); -+ - cfgOid = simple_heap_insert(cfgRel, tup); - - CatalogUpdateIndexes(cfgRel, tup); -@@ -1489,6 +1538,9 @@ RenameTSConfiguration(List *oldname, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux checks */ -+ sepgsql_ts_config_alter_rename(cfgId, newname); -+ - namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname); - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); -@@ -1549,6 +1601,9 @@ RemoveTSConfigurations(DropStmt *drop) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, - NameListToString(names)); - -+ /* SELinux checks */ -+ sepgsql_ts_config_drop(cfgOid, false); -+ - object.classId = TSConfigRelationId; - object.objectId = cfgOid; - object.objectSubId = 0; -@@ -1656,6 +1711,8 @@ AlterTSConfigurationOwner(List *name, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux checks */ -+ sepgsql_ts_config_alter(cfgId); - - form->cfgowner = newOwnerId; - -@@ -1693,6 +1750,9 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, - NameListToString(stmt->cfgname)); - -+ /* SELinux checks */ -+ sepgsql_ts_config_alter(HeapTupleGetOid(tup)); -+ - relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock); - - /* Add or drop mappings */ -diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c -index abde562..a5cf445 100644 ---- a/src/backend/commands/typecmds.c -+++ b/src/backend/commands/typecmds.c -@@ -56,6 +56,7 @@ - #include "parser/parse_expr.h" - #include "parser/parse_func.h" - #include "parser/parse_type.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -143,6 +144,8 @@ DefineType(List *names, List *parameters) - char *array_type; - Oid array_oid; - Oid typoid; -+ Oid type_replaced; -+ Oid securityId; - Oid resulttype; - ListCell *pl; - -@@ -520,6 +523,15 @@ DefineType(List *names, List *parameters) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - NameListToString(analyzeName)); - #endif -+ /* SELinux checks */ -+ type_replaced = GetSysCacheOid2(TYPENAMENSP, -+ CStringGetDatum(typeName), -+ ObjectIdGetDatum(typeNamespace)); -+ securityId = sepgsql_type_create(typeName, type_replaced, -+ typeNamespace, TYPTYPE_BASE, -+ inputOid, outputOid, -+ receiveOid, sendOid, -+ typmodinOid, typmodoutOid, analyzeOid); - - array_oid = AssignTypeArrayOid(); - -@@ -562,7 +574,8 @@ DefineType(List *names, List *parameters) - storage, /* TOAST strategy */ - -1, /* typMod (Domains only) */ - 0, /* Array Dimensions of typbasetype */ -- false); /* Type NOT NULL */ -+ false, /* Type NOT NULL */ -+ securityId); /* security-id of the type */ - - /* - * Create the array type that goes with it. -@@ -601,7 +614,8 @@ DefineType(List *names, List *parameters) - 'x', /* ARRAY is always toastable */ - -1, /* typMod (Domains only) */ - 0, /* Array dimensions of typbasetype */ -- false); /* Type NOT NULL */ -+ false, /* Type NOT NULL */ -+ securityId); /* security-id of the type */ - - pfree(array_type); - } -@@ -668,6 +682,9 @@ RemoveTypes(DropStmt *drop) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, - format_type_be(typeoid)); - -+ /* SELinux checks */ -+ sepgsql_type_drop(typeoid, false); -+ - if (drop->removeType == OBJECT_DOMAIN) - { - /* Check that this is actually a domain */ -@@ -766,6 +783,7 @@ DefineDomain(CreateDomainStmt *stmt) - Oid basetypeoid; - Oid domainoid; - Oid old_type_oid; -+ Oid securityId; - Form_pg_type baseType; - int32 basetypeMod; - -@@ -1011,6 +1029,13 @@ DefineDomain(CreateDomainStmt *stmt) - } - } - -+ /* SELinux checks */ -+ securityId = sepgsql_type_create(domainName, InvalidOid, -+ domainNamespace, TYPTYPE_DOMAIN, -+ inputProcedure, outputProcedure, -+ receiveProcedure, sendProcedure, -+ InvalidOid, InvalidOid, analyzeProcedure); -+ - /* - * Have TypeCreate do all the real work. - */ -@@ -1044,7 +1069,8 @@ DefineDomain(CreateDomainStmt *stmt) - storage, /* TOAST strategy */ - basetypeMod, /* typeMod value */ - typNDims, /* Array dimensions for base type */ -- typNotNull); /* Type NOT NULL */ -+ typNotNull, /* Type NOT NULL */ -+ securityId); /* security-id of the type */ - - /* - * Process constraints which refer to the domain ID returned by TypeCreate -@@ -1094,6 +1120,7 @@ DefineEnum(CreateEnumStmt *stmt) - AclResult aclresult; - Oid old_type_oid; - Oid enumArrayOid; -+ Oid securityId; - - /* Convert list of names to a name and namespace */ - enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName, -@@ -1120,6 +1147,13 @@ DefineEnum(CreateEnumStmt *stmt) - errmsg("type \"%s\" already exists", enumName))); - } - -+ /* SELinux checks */ -+ securityId = sepgsql_type_create(enumName, old_type_oid, -+ enumNamespace, TYPTYPE_ENUM, -+ F_ENUM_IN, F_ENUM_OUT, -+ F_ENUM_RECV, F_ENUM_SEND, -+ InvalidOid, InvalidOid, InvalidOid); -+ - enumArrayOid = AssignTypeArrayOid(); - - /* Create the pg_type entry */ -@@ -1153,7 +1187,8 @@ DefineEnum(CreateEnumStmt *stmt) - 'p', /* TOAST strategy always plain */ - -1, /* typMod (Domains only) */ - 0, /* Array dimensions of typbasetype */ -- false); /* Type NOT NULL */ -+ false, /* Type NOT NULL */ -+ securityId); /* security-id of the type */ - - /* Enter the enum's values into pg_enum */ - EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid); -@@ -1192,7 +1227,8 @@ DefineEnum(CreateEnumStmt *stmt) - 'x', /* ARRAY is always toastable */ - -1, /* typMod (Domains only) */ - 0, /* Array dimensions of typbasetype */ -- false); /* Type NOT NULL */ -+ false, /* Type NOT NULL */ -+ securityId); /* security-id of the type */ - - pfree(enumArrayName); - } -@@ -1585,6 +1621,8 @@ AlterDomainDefault(List *names, Node *defaultRaw) - /* Check it's a domain and check user has permission for ALTER DOMAIN */ - checkDomainOwner(tup, typename); - -+ sepgsql_type_alter(domainoid); -+ - /* Setup new tuple */ - MemSet(new_record, (Datum) 0, sizeof(new_record)); - MemSet(new_record_nulls, false, sizeof(new_record_nulls)); -@@ -1711,6 +1749,8 @@ AlterDomainNotNull(List *names, bool notNull) - /* Check it's a domain and check user has permission for ALTER DOMAIN */ - checkDomainOwner(tup, typename); - -+ sepgsql_type_alter(domainoid); -+ - /* Is the domain already set to the desired constraint? */ - if (typTup->typnotnull == notNull) - { -@@ -1810,6 +1850,8 @@ AlterDomainDropConstraint(List *names, const char *constrName, - /* Check it's a domain and check user has permission for ALTER DOMAIN */ - checkDomainOwner(tup, typename); - -+ sepgsql_type_alter(domainoid); -+ - /* Grab an appropriate lock on the pg_constraint relation */ - conrel = heap_open(ConstraintRelationId, RowExclusiveLock); - -@@ -1884,6 +1926,8 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) - /* Check it's a domain and check user has permission for ALTER DOMAIN */ - checkDomainOwner(tup, typename); - -+ sepgsql_type_alter(domainoid); -+ - if (!IsA(newConstraint, Constraint)) - elog(ERROR, "unrecognized node type: %d", - (int) nodeTag(newConstraint)); -@@ -2507,6 +2551,9 @@ RenameType(List *names, const char *newTypeName) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, - format_type_be(typeOid)); - -+ /* SELinux checks */ -+ sepgsql_type_alter_rename(typeOid, newTypeName); -+ - /* - * If it's a composite type, we need to check that it really is a - * free-standing composite type, and not a table's rowtype. We want people -@@ -2627,6 +2674,8 @@ AlterTypeOwner(List *names, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(typTup->typnamespace)); - } -+ /* SELinux checks */ -+ sepgsql_type_alter(HeapTupleGetOid(tup)); - - /* - * If it's a composite type, invoke ATExecChangeOwner so that we fix -@@ -2731,6 +2780,9 @@ AlterTypeNamespace(List *names, const char *newschema) - /* get schema OID and check its permissions */ - nspOid = LookupCreationNamespace(newschema); - -+ /* SELinux checks */ -+ sepgsql_type_alter_schema(typeOid, nspOid); -+ - /* don't allow direct alteration of array types */ - elemOid = get_element_type(typeOid); - if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid) -@@ -2882,3 +2934,100 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, - if (OidIsValid(arrayOid)) - AlterTypeNamespaceInternal(arrayOid, nspOid, true, true); - } -+ -+/* -+ * ALTER TYPE xxx SECURITY LABEL TO ... -+ */ -+void -+AlterTypeSecLabelInternal(Oid typeOid, Oid securityId) -+{ -+ Relation typeRel; -+ Form_pg_type typeForm; -+ HeapTuple tuple; -+ -+ typeRel = heap_open(TypeRelationId, RowExclusiveLock); -+ -+ tuple = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for type %u", typeOid); -+ typeForm = (Form_pg_type) GETSTRUCT(tuple); -+ -+ /* update it */ -+ HeapTupleSetSecid(tuple, securityId); -+ -+ simple_heap_update(typeRel, &tuple->t_self, tuple); -+ -+ CatalogUpdateIndexes(typeRel, tuple); -+ -+ /* if it is a composit type, update pg_class too */ -+ if (OidIsValid(typeForm->typrelid)) -+ { -+ Oid classSecId = seclabelMoveSecid(RelationRelationId, -+ TypeRelationId, -+ securityId); -+ AlterRelationSecLabelInternal(typeForm->typrelid, classSecId, 0); -+ } -+ -+ /* if it has an array type, update that too */ -+ if (OidIsValid(typeForm->typarray)) -+ AlterTypeSecLabelInternal(typeForm->typarray, securityId); -+ -+ heap_freetuple(tuple); -+ -+ heap_close(typeRel, RowExclusiveLock); -+} -+ -+void -+AlterTypeSecLabel(List *names, char *new_label) -+{ -+ Form_pg_type typeForm; -+ HeapTuple typtup; -+ TypeName *typename; -+ Oid typeOid; -+ Oid securityId; -+ -+ /* resolve type name */ -+ typename = makeTypeNameFromNameList(names); -+ typtup = LookupTypeName(NULL, typename, NULL); -+ if (!HeapTupleIsValid(typtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_OBJECT), -+ errmsg("type \"%s\" does not exist", -+ TypeNameToString(typename)))); -+ -+ typeForm = (Form_pg_type) GETSTRUCT(typtup); -+ typeOid = HeapTupleGetOid(typtup); -+ -+ /* -+ * If it's a composite type, we need to check that it really is a -+ * free-standing composite type, and not a table's rowtype. We want people -+ * to use ALTER TABLE not ALTER TYPE for that case. -+ */ -+ if (typeForm->typtype == TYPTYPE_COMPOSITE && -+ get_rel_relkind(typeForm->typrelid) != RELKIND_COMPOSITE_TYPE) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("%s is a table's row type", -+ format_type_be(typeOid)), -+ errhint("Use ALTER TABLE instead."))); -+ -+ /* don't allow direct alteration of array types, either */ -+ if (OidIsValid(typeForm->typelem) && -+ get_array_type(typeForm->typelem) == typeOid) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("cannot alter array type %s", -+ format_type_be(typeOid)))); -+ -+ /* DAC permission checks */ -+ if (!pg_type_ownercheck(typeOid, GetUserId())) -+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, -+ format_type_be(typeOid)); -+ -+ /* SELinux checks */ -+ securityId = sepgsql_type_relabel(typeOid, new_label); -+ -+ AlterTypeSecLabelInternal(typeOid, securityId); -+ -+ ReleaseSysCache(typtup); -+} -diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c -index cdf7dc5..f6aee43 100644 ---- a/src/backend/commands/user.c -+++ b/src/backend/commands/user.c -@@ -21,11 +21,13 @@ - #include "catalog/pg_authid.h" - #include "catalog/pg_database.h" - #include "catalog/pg_db_role_setting.h" -+#include "catalog/pg_seclabel.h" - #include "commands/comment.h" - #include "commands/dbcommands.h" - #include "commands/user.h" - #include "libpq/md5.h" - #include "miscadmin.h" -+#include "sepgsql/hooks.h" - #include "storage/lmgr.h" - #include "utils/acl.h" - #include "utils/builtins.h" -@@ -111,6 +113,7 @@ CreateRole(CreateRoleStmt *stmt) - DefElem *drolemembers = NULL; - DefElem *dadminmembers = NULL; - DefElem *dvalidUntil = NULL; -+ Oid securityId; - - /* The defaults can vary depending on the original statement type */ - switch (stmt->stmt_type) -@@ -279,6 +282,9 @@ CreateRole(CreateRoleStmt *stmt) - errmsg("permission denied to create role"))); - } - -+ /* SELinux checks */ -+ securityId = sepgsql_role_create(stmt->role); -+ - if (strcmp(stmt->role, "public") == 0 || - strcmp(stmt->role, "none") == 0) - ereport(ERROR, -@@ -365,6 +371,8 @@ CreateRole(CreateRoleStmt *stmt) - - tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls); - -+ HeapTupleSetSecid(tuple, securityId); -+ - /* - * Insert new record in the pg_authid table - */ -@@ -606,6 +614,8 @@ AlterRole(AlterRoleStmt *stmt) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied"))); - } -+ /* SELinux checks */ -+ sepgsql_role_alter(roleid); - - /* Convert validuntil to internal form */ - if (validUntil) -@@ -791,6 +801,8 @@ AlterRoleSet(AlterRoleSetStmt *stmt) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied"))); - } -+ /* SELinux checks */ -+ sepgsql_role_alter(HeapTupleGetOid(roletuple)); - - /* look up and lock the database, if specified */ - if (stmt->database != NULL) -@@ -886,6 +898,9 @@ DropRole(DropRoleStmt *stmt) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to drop superusers"))); - -+ /* SELinux checks */ -+ sepgsql_role_drop(roleid, false); -+ - /* - * Lock the role, so nobody can add dependencies to her while we drop - * her. We keep the lock until the end of transaction. -@@ -1282,6 +1297,9 @@ AddRoleMems(const char *rolename, Oid roleid, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to set grantor"))); - -+ /* SELinux checks */ -+ sepgsql_role_grant(roleid, true, memberIds); -+ - pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock); - pg_authmem_dsc = RelationGetDescr(pg_authmem_rel); - -@@ -1412,6 +1430,8 @@ DelRoleMems(const char *rolename, Oid roleid, - errmsg("must have admin option on role \"%s\"", - rolename))); - } -+ /* SELinux checks */ -+ sepgsql_role_grant(roleid, false, memberIds); - - pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock); - pg_authmem_dsc = RelationGetDescr(pg_authmem_rel); -diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c -index e77430e..3dfc7d4 100644 ---- a/src/backend/commands/vacuum.c -+++ b/src/backend/commands/vacuum.c -@@ -957,6 +957,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound, - */ - if (vacstmt->options & VACOPT_FULL) - { -+ /* Also reclaim unreferenced security labels */ -+ seclabelRelationReclaim(relid); -+ - /* close relation before vacuuming, but hold lock until commit */ - relation_close(onerel, NoLock); - onerel = NULL; -diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c -index c291529..dd0b12a 100644 ---- a/src/backend/commands/view.c -+++ b/src/backend/commands/view.c -@@ -28,6 +28,7 @@ - #include "rewrite/rewriteDefine.h" - #include "rewrite/rewriteManip.h" - #include "rewrite/rewriteSupport.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -167,6 +168,9 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(rel)); - -+ /* SELinux checks */ -+ sepgsql_view_replace(viewOid); -+ - /* Also check it's not in use already */ - CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW"); - -diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c -index da22709..cbd4932 100644 ---- a/src/backend/executor/execJunk.c -+++ b/src/backend/executor/execJunk.c -@@ -58,7 +58,8 @@ - * An optional resultSlot can be passed as well. - */ - JunkFilter * --ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) -+ExecInitJunkFilter(List *targetList, bool hasoid, bool hassecid, -+ TupleTableSlot *slot) - { - JunkFilter *junkfilter; - TupleDesc cleanTupType; -@@ -70,7 +71,7 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) - /* - * Compute the tuple descriptor for the cleaned tuple. - */ -- cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); -+ cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hassecid); - - /* - * Use the given slot, or make a new slot if we weren't given one. -diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c -index 6d4a773..a8ad9a3 100644 ---- a/src/backend/executor/execMain.c -+++ b/src/backend/executor/execMain.c -@@ -38,6 +38,7 @@ - #include "access/xact.h" - #include "catalog/heap.h" - #include "catalog/namespace.h" -+#include "catalog/pg_seclabel.h" - #include "catalog/toasting.h" - #include "commands/tablespace.h" - #include "commands/trigger.h" -@@ -47,11 +48,13 @@ - #include "optimizer/clauses.h" - #include "parser/parse_clause.h" - #include "parser/parsetree.h" -+#include "sepgsql/hooks.h" - #include "storage/bufmgr.h" - #include "storage/lmgr.h" - #include "storage/smgr.h" - #include "tcop/utility.h" - #include "utils/acl.h" -+#include "utils/guc.h" - #include "utils/lsyscache.h" - #include "utils/memutils.h" - #include "utils/snapmgr.h" -@@ -414,7 +417,16 @@ ExecCheckRTPerms(List *rangeTable) - - foreach(l, rangeTable) - { -- ExecCheckRTEPerms((RangeTblEntry *) lfirst(l)); -+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); -+ -+ ExecCheckRTEPerms(rte); -+ -+ if (rte->rtekind == RTE_RELATION) -+ sepgsql_relation_perms(rte->relid, -+ rte->requiredPerms, -+ rte->selectedCols, -+ rte->modifiedCols, -+ true); - } - } - -@@ -826,6 +838,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) - - j = ExecInitJunkFilter(planstate->plan->targetlist, - tupType->tdhasoid, -+ tupType->tdhassecid, - ExecInitExtraTupleSlot(estate)); - estate->es_junkFilter = j; - -@@ -1062,6 +1075,37 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids) - return false; - } - -+/* -+ * ExecContextForcesSecids -+ * -+ * It is same with ExecContextForcesOids, except for it checks space -+ * for security id of the tuples. -+ */ -+bool -+ExecContextForcesSecids(PlanState *planstate, bool *hassecid) -+{ -+ ResultRelInfo *ri = planstate->state->es_result_relation_info; -+ -+ if (ri != NULL) -+ { -+ Relation rel = ri->ri_RelationDesc; -+ -+ if (rel != NULL) -+ { -+ *hassecid = RelationGetForm(rel)->relhassecids; -+ return true; -+ } -+ } -+ -+ if (planstate->state->es_select_into) -+ { -+ *hassecid = default_with_secids; -+ return true; -+ } -+ -+ return false; -+} -+ - /* ---------------------------------------------------------------- - * ExecEndPlan - * -@@ -2130,6 +2174,7 @@ OpenIntoRel(QueryDesc *queryDesc) - Oid intoRelationId; - TupleDesc tupdesc; - DR_intorel *myState; -+ Oid *secLabels; - static char *validnsps[] = HEAP_RELOPT_NAMESPACES; - - Assert(into); -@@ -2201,6 +2246,14 @@ OpenIntoRel(QueryDesc *queryDesc) - get_tablespace_name(tablespaceId)); - } - -+ /* SELinux checks */ -+ secLabels = sepgsql_relation_create(intoName, -+ RELKIND_RELATION, -+ queryDesc->tupDesc, -+ namespaceId, -+ NIL, -+ true); -+ - /* Parse and validate any reloptions */ - reloptions = transformRelOptions((Datum) 0, - into->options, -@@ -2231,7 +2284,8 @@ OpenIntoRel(QueryDesc *queryDesc) - into->onCommit, - reloptions, - true, -- allowSystemTableMods); -+ allowSystemTableMods, -+ secLabels); - - FreeTupleDesc(tupdesc); - -@@ -2355,6 +2409,11 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self) - */ - if (myState->rel->rd_rel->relhasoids) - HeapTupleSetOid(tuple, InvalidOid); -+ if (myState->rel->rd_rel->relhassecids) -+ HeapTupleSetSecid(tuple, InvalidOid); -+ -+ /* SELinux checks */ -+ sepgsql_tuple_insert(myState->rel, tuple); - - heap_insert(myState->rel, - tuple, -diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c -index bfcce0f..a07b314 100644 ---- a/src/backend/executor/execQual.c -+++ b/src/backend/executor/execQual.c -@@ -49,6 +49,7 @@ - #include "optimizer/planner.h" - #include "parser/parse_coerce.h" - #include "pgstat.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -1177,6 +1178,9 @@ init_fcache(Oid foid, FuncExprState *fcache, - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); - -+ /* SELinux checks */ -+ sepgsql_proc_execute(foid); -+ - /* - * Safety check on nargs. Under normal circumstances this should never - * fail, as parser should check sooner. But possibly it might fail if -@@ -1221,7 +1225,7 @@ init_fcache(Oid foid, FuncExprState *fcache, - else if (functypclass == TYPEFUNC_SCALAR) - { - /* Base data type, i.e. scalar */ -- tupdesc = CreateTemplateTupleDesc(1, false); -+ tupdesc = CreateTemplateTupleDesc(1, false, false); - TupleDescInitEntry(tupdesc, - (AttrNumber) 1, - NULL, -@@ -2111,7 +2115,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr, - /* - * Scalar type, so make a single-column descriptor - */ -- tupdesc = CreateTemplateTupleDesc(1, false); -+ tupdesc = CreateTemplateTupleDesc(1, false, false); - TupleDescInitEntry(tupdesc, - (AttrNumber) 1, - "column", -@@ -4118,6 +4122,9 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(acoerce->elemfuncid)); - -+ /* SELinux checks */ -+ sepgsql_proc_execute(acoerce->elemfuncid); -+ - /* Set up the primary fmgr lookup information */ - fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), - econtext->ecxt_per_query_memory); -diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c -index fa5ff2d..fb9093c 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 "parser/parsetree.h" - #include "utils/memutils.h" - - -@@ -114,6 +115,9 @@ ExecScan(ScanState *node, - ProjectionInfo *projInfo; - ExprDoneCond isDone; - TupleTableSlot *resultSlot; -+ Scan *scan = (Scan *)node->ps.plan; -+ EState *estate = node->ps.state; -+ RangeTblEntry *rte = rt_fetch(scan->scanrelid, estate->es_range_table); - - /* - * Fetch data from node -@@ -125,7 +129,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 && !rte->rowlvPerms) - return ExecScanFetch(node, accessMtd, recheckMtd); - - /* -@@ -189,9 +193,20 @@ ExecScan(ScanState *node, - * when the qual is nil ... saves only a few cycles, but they add up - * ... - */ -- if (!qual || ExecQual(qual, econtext, false)) -+ if (sepgsql_rowlv_quals(node->ss_currentRelation, -+ rte->rowlvPerms, slot, false) && -+ (!qual || ExecQual(qual, econtext, false))) - { - /* -+ * NOTE: When FK checks, the Row-level features needs to raise -+ * an error after evaluation of all the given quals, to avoid -+ * incorrect error reports about unrelated tuples. -+ * We assume FK does not use malicious functions in the quals. -+ */ -+ sepgsql_rowlv_quals(node->ss_currentRelation, -+ rte->rowlvPerms, slot, true); -+ -+ /* - * Found a satisfactory scan tuple. - */ - if (projInfo) -@@ -258,6 +273,7 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc - int numattrs = tupdesc->natts; - int attrno; - bool hasoid; -+ bool hassecid; - ListCell *tlist_item = list_head(tlist); - - /* Check the tlist attributes */ -@@ -307,6 +323,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc - if (ExecContextForcesOids(ps, &hasoid) && - hasoid != tupdesc->tdhasoid) - return false; -+ if (ExecContextForcesSecids(ps, &hassecid) && -+ hassecid != tupdesc->tdhassecid) -+ return false; - - return true; - } -diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c -index e2ee706..d4d216b 100644 ---- a/src/backend/executor/execTuples.c -+++ b/src/backend/executor/execTuples.c -@@ -98,7 +98,7 @@ - - - static TupleDesc ExecTypeFromTLInternal(List *targetList, -- bool hasoid, bool skipjunk); -+ bool hasoid, bool hassecid, bool skipjunk); - - - /* ---------------------------------------------------------------- -@@ -899,9 +899,9 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType) - * ---------------------------------------------------------------- - */ - TupleDesc --ExecTypeFromTL(List *targetList, bool hasoid) -+ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid) - { -- return ExecTypeFromTLInternal(targetList, hasoid, false); -+ return ExecTypeFromTLInternal(targetList, hasoid, hassecid, false); - } - - /* ---------------------------------------------------------------- -@@ -911,13 +911,14 @@ ExecTypeFromTL(List *targetList, bool hasoid) - * ---------------------------------------------------------------- - */ - TupleDesc --ExecCleanTypeFromTL(List *targetList, bool hasoid) -+ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid) - { -- return ExecTypeFromTLInternal(targetList, hasoid, true); -+ return ExecTypeFromTLInternal(targetList, hasoid, hassecid, true); - } - - static TupleDesc --ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) -+ExecTypeFromTLInternal(List *targetList, -+ bool hasoid, bool hassecid, bool skipjunk) - { - TupleDesc typeInfo; - ListCell *l; -@@ -928,7 +929,7 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) - len = ExecCleanTargetListLength(targetList); - else - len = ExecTargetListLength(targetList); -- typeInfo = CreateTemplateTupleDesc(len, hasoid); -+ typeInfo = CreateTemplateTupleDesc(len, hasoid, hassecid); - - foreach(l, targetList) - { -@@ -960,7 +961,7 @@ ExecTypeFromExprList(List *exprList) - int cur_resno = 1; - char fldname[NAMEDATALEN]; - -- typeInfo = CreateTemplateTupleDesc(list_length(exprList), false); -+ typeInfo = CreateTemplateTupleDesc(list_length(exprList), false, false); - - foreach(l, exprList) - { -diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c -index 5886c1b..50f91f5 100644 ---- a/src/backend/executor/execUtils.c -+++ b/src/backend/executor/execUtils.c -@@ -444,6 +444,7 @@ void - ExecAssignResultTypeFromTL(PlanState *planstate) - { - bool hasoid; -+ bool hassecid; - TupleDesc tupDesc; - - if (ExecContextForcesOids(planstate, &hasoid)) -@@ -456,12 +457,15 @@ ExecAssignResultTypeFromTL(PlanState *planstate) - hasoid = false; - } - -+ if (!ExecContextForcesSecids(planstate, &hassecid)) -+ hassecid = false; -+ - /* - * ExecTypeFromTL needs the parse-time representation of the tlist, not a - * list of ExprStates. This is good because some plan nodes don't bother - * to set up planstate->targetlist ... - */ -- tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid); -+ tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid, hassecid); - ExecAssignResultType(planstate, tupDesc); - } - -diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c -index 75fc173..99cb650 100644 ---- a/src/backend/executor/functions.c -+++ b/src/backend/executor/functions.c -@@ -1153,7 +1153,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, - - /* Set up junk filter if needed */ - if (junkFilter) -- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); -+ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); - } - else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) - { -@@ -1197,7 +1197,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, - } - /* Set up junk filter if needed */ - if (junkFilter) -- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); -+ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); - return false; /* NOT returning whole tuple */ - } - } -@@ -1210,7 +1210,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 74fc87a..4b222b1 100644 ---- a/src/backend/executor/nodeAgg.c -+++ b/src/backend/executor/nodeAgg.c -@@ -89,6 +89,7 @@ - #include "optimizer/tlist.h" - #include "parser/parse_agg.h" - #include "parser/parse_coerce.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -1641,6 +1642,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) - get_func_name(finalfn_oid)); - } - } -+ /* SELinux checks */ -+ sepgsql_aggregate_execute(aggref->aggfnoid); - - /* resolve actual type of transition state, if polymorphic */ - aggtranstype = aggform->aggtranstype; -@@ -1722,7 +1725,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) - * Get a tupledesc corresponding to the inputs (including sort - * expressions) of the agg. - */ -- peraggstate->evaldesc = ExecTypeFromTL(aggref->args, false); -+ peraggstate->evaldesc = ExecTypeFromTL(aggref->args, false, false); - - /* Create slot we're going to do argument evaluation in */ - peraggstate->evalslot = ExecInitExtraTupleSlot(estate); -diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c -index 6989961..471653f 100644 ---- a/src/backend/executor/nodeFunctionscan.c -+++ b/src/backend/executor/nodeFunctionscan.c -@@ -178,7 +178,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) - /* Base data type, i.e. scalar */ - char *attname = strVal(linitial(node->funccolnames)); - -- tupdesc = CreateTemplateTupleDesc(1, false); -+ tupdesc = CreateTemplateTupleDesc(1, false, false); - TupleDescInitEntry(tupdesc, - (AttrNumber) 1, - attname, -diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c -index 948f580..7d9f4f4 100644 ---- a/src/backend/executor/nodeMergejoin.c -+++ b/src/backend/executor/nodeMergejoin.c -@@ -98,6 +98,7 @@ - #include "executor/execdefs.h" - #include "executor/nodeMergejoin.h" - #include "miscadmin.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/lsyscache.h" - #include "utils/memutils.h" -@@ -224,6 +225,9 @@ MJExamineQuals(List *mergeclauses, - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(cmpproc)); - -+ /* SELinux permissions */ -+ sepgsql_proc_execute(cmpproc); -+ - /* Set up the fmgr lookup information */ - fmgr_info(cmpproc, &(clause->cmpfinfo)); - -diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c -index f4b2b16..62c76b5 100644 ---- a/src/backend/executor/nodeModifyTable.c -+++ b/src/backend/executor/nodeModifyTable.c -@@ -38,11 +38,13 @@ - #include "postgres.h" - - #include "access/xact.h" -+#include "catalog/pg_seclabel.h" - #include "commands/trigger.h" - #include "executor/executor.h" - #include "executor/nodeModifyTable.h" - #include "miscadmin.h" - #include "nodes/nodeFuncs.h" -+#include "sepgsql/hooks.h" - #include "storage/bufmgr.h" - #include "utils/builtins.h" - #include "utils/memutils.h" -@@ -160,7 +162,8 @@ ExecProcessReturning(ProjectionInfo *projectReturning, - static TupleTableSlot * - ExecInsert(TupleTableSlot *slot, - TupleTableSlot *planSlot, -- EState *estate) -+ EState *estate, -+ Oid securityId) - { - HeapTuple tuple; - ResultRelInfo *resultRelInfo; -@@ -194,6 +197,8 @@ ExecInsert(TupleTableSlot *slot, - */ - if (resultRelationDesc->rd_rel->relhasoids) - HeapTupleSetOid(tuple, InvalidOid); -+ if (resultRelationDesc->rd_rel->relhassecids) -+ HeapTupleSetSecid(tuple, securityId); - - /* BEFORE ROW INSERT Triggers */ - if (resultRelInfo->ri_TrigDesc && -@@ -224,6 +229,8 @@ ExecInsert(TupleTableSlot *slot, - tuple = newtuple; - } - } -+ /* SELinux checks */ -+ sepgsql_tuple_insert(resultRelationDesc, tuple); - - /* - * Check the constraints of the tuple -@@ -421,7 +428,8 @@ ExecUpdate(ItemPointer tupleid, - TupleTableSlot *slot, - TupleTableSlot *planSlot, - EPQState *epqstate, -- EState *estate) -+ EState *estate, -+ Oid securityId) - { - HeapTuple tuple; - ResultRelInfo *resultRelInfo; -@@ -449,6 +457,13 @@ ExecUpdate(ItemPointer tupleid, - resultRelInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelInfo->ri_RelationDesc; - -+ /* -+ * If the result relation has writable system attributes, -+ * we store user given value (or InvalidOid) on the tuple. -+ */ -+ if (resultRelationDesc->rd_rel->relhassecids) -+ HeapTupleSetSecid(tuple, securityId); -+ - /* BEFORE ROW UPDATE Triggers */ - if (resultRelInfo->ri_TrigDesc && - resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) -@@ -479,6 +494,8 @@ ExecUpdate(ItemPointer tupleid, - tuple = newtuple; - } - } -+ /* SELinux checks */ -+ sepgsql_tuple_update(resultRelationDesc, tupleid, tuple); - - /* - * Check the constraints of the tuple -@@ -635,6 +652,42 @@ fireASTriggers(ModifyTableState *node) - } - } - -+/* -+ * FetchWritableSecLabel -+ * -+ * It moves user given security label into slot-> -+ */ -+static Oid -+FetchWritableSecLabel(Relation relation, -+ JunkFilter *junkfilter, -+ TupleTableSlot *slot) -+{ -+ Oid securityId = InvalidOid; -+ AttrNumber attno; -+ Datum datum; -+ bool isnull; -+ char *label; -+ -+ /* -+ * If no explicit label was given, set a default label later -+ */ -+ attno = ExecFindJunkAttribute(junkfilter, "security_label"); -+ if (attno != InvalidAttrNumber && !ignore_security_label_input) -+ { -+ datum = ExecGetJunkAttribute(slot, attno, &isnull); -+ if (isnull) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set NULL on \"security_label\""))); -+ -+ label = TextDatumGetCString(datum); -+ -+ securityId = seclabelTransInput(RelationGetRelid(relation), label); -+ -+ pfree(label); -+ } -+ return securityId; -+} - - /* ---------------------------------------------------------------- - * ExecModifyTable -@@ -682,6 +735,8 @@ ExecModifyTable(ModifyTableState *node) - */ - for (;;) - { -+ Oid securityId = InvalidOid; -+ - /* - * Reset the per-output-tuple exprcontext. This is needed because - * triggers expect to use that context as workspace. It's a bit ugly -@@ -714,6 +769,14 @@ ExecModifyTable(ModifyTableState *node) - - if (junkfilter != NULL) - { -+ Relation targetRel -+ = estate->es_result_relation_info->ri_RelationDesc; -+ -+ /* -+ * extract writable system column -+ */ -+ securityId = FetchWritableSecLabel(targetRel, junkfilter, slot); -+ - /* - * extract the 'ctid' junk attribute. - */ -@@ -743,11 +806,11 @@ ExecModifyTable(ModifyTableState *node) - switch (operation) - { - case CMD_INSERT: -- slot = ExecInsert(slot, planSlot, estate); -+ slot = ExecInsert(slot, planSlot, estate, securityId); - break; - case CMD_UPDATE: - slot = ExecUpdate(tupleid, slot, planSlot, -- &node->mt_epqstate, estate); -+ &node->mt_epqstate, estate, securityId); - break; - case CMD_DELETE: - slot = ExecDelete(tupleid, planSlot, -@@ -855,7 +918,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) - * RETURNING list. We assume the rest will look the same. - */ - tupDesc = ExecTypeFromTL((List *) linitial(node->returningLists), -- false); -+ false, false); - - /* Set up a slot for the output of the RETURNING projection(s) */ - ExecInitResultTupleSlot(estate, &mtstate->ps); -@@ -889,7 +952,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) - * We still must construct a dummy result tuple type, because InitPlan - * expects one (maybe should change that?). - */ -- tupDesc = ExecTypeFromTL(NIL, false); -+ tupDesc = ExecTypeFromTL(NIL, false, false); - ExecInitResultTupleSlot(estate, &mtstate->ps); - ExecAssignResultType(&mtstate->ps, tupDesc); - -@@ -986,6 +1049,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) - - j = ExecInitJunkFilter(subplan->targetlist, - resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, -+ resultRelInfo->ri_RelationDesc->rd_att->tdhassecid, - ExecInitExtraTupleSlot(estate)); - - if (operation == CMD_UPDATE || operation == CMD_DELETE) -diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c -index d397b13..90e2e65 100644 ---- a/src/backend/executor/nodeSubplan.c -+++ b/src/backend/executor/nodeSubplan.c -@@ -859,7 +859,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) - * (hack alert!). The righthand expressions will be evaluated in our - * own innerecontext. - */ -- tupDesc = ExecTypeFromTL(leftptlist, false); -+ tupDesc = ExecTypeFromTL(leftptlist, false, false); - slot = ExecInitExtraTupleSlot(estate); - ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, -@@ -867,7 +867,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) - slot, - NULL); - -- tupDesc = ExecTypeFromTL(rightptlist, false); -+ tupDesc = ExecTypeFromTL(rightptlist, false, false); - slot = ExecInitExtraTupleSlot(estate); - ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, -diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c -index de6d038..2ee0a8f 100644 ---- a/src/backend/executor/nodeWindowAgg.c -+++ b/src/backend/executor/nodeWindowAgg.c -@@ -43,6 +43,7 @@ - #include "optimizer/clauses.h" - #include "parser/parse_agg.h" - #include "parser/parse_coerce.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/datum.h" -@@ -1554,6 +1555,12 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(wfunc->winfnoid)); - -+ /* SELinux checks */ -+ if (wfunc->winagg) -+ sepgsql_aggregate_execute(wfunc->winfnoid); -+ else -+ sepgsql_proc_execute(wfunc->winfnoid); -+ - /* Fill in the perfuncstate data */ - perfuncstate->wfuncstate = wfuncstate; - perfuncstate->wfunc = wfunc; -diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c -index 1ffb1b2..623b2dd 100644 ---- a/src/backend/executor/spi.c -+++ b/src/backend/executor/spi.c -@@ -767,6 +767,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, - mtuple->t_tableOid = tuple->t_tableOid; - if (rel->rd_att->tdhasoid) - HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); -+ if (rel->rd_att->tdhassecid) -+ HeapTupleSetSecid(mtuple, HeapTupleGetSecid(tuple)); - } - else - { -@@ -795,7 +797,8 @@ SPI_fnumber(TupleDesc tupdesc, const char *fname) - return res + 1; - } - -- sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ ); -+ /* "oid" and "security_label" will be accepted */ -+ sysatt = SystemAttributeByName(fname, true, true); - if (sysatt != NULL) - return sysatt->attnum; - -@@ -820,7 +823,7 @@ SPI_fname(TupleDesc tupdesc, int fnumber) - if (fnumber > 0) - att = tupdesc->attrs[fnumber - 1]; - else -- att = SystemAttributeDefinition(fnumber, true); -+ att = SystemAttributeDefinition(fnumber, true, true); - - return pstrdup(NameStr(att->attname)); - } -@@ -852,7 +855,7 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) - if (fnumber > 0) - typoid = tupdesc->attrs[fnumber - 1]->atttypid; - else -- typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; -+ typoid = (SystemAttributeDefinition(fnumber, true, true))->atttypid; - - getTypeOutputInfo(typoid, &foutoid, &typisvarlena); - -@@ -909,7 +912,7 @@ SPI_gettype(TupleDesc tupdesc, int fnumber) - if (fnumber > 0) - typoid = tupdesc->attrs[fnumber - 1]->atttypid; - else -- typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; -+ typoid = (SystemAttributeDefinition(fnumber, true, true))->atttypid; - - typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid)); - -@@ -939,7 +942,7 @@ SPI_gettypeid(TupleDesc tupdesc, int fnumber) - if (fnumber > 0) - return tupdesc->attrs[fnumber - 1]->atttypid; - else -- return (SystemAttributeDefinition(fnumber, true))->atttypid; -+ return (SystemAttributeDefinition(fnumber, true, true))->atttypid; - } - - char * -diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c -index 464183d..67d0316 100644 ---- a/src/backend/libpq/be-fsstubs.c -+++ b/src/backend/libpq/be-fsstubs.c -@@ -46,6 +46,7 @@ - #include "libpq/be-fsstubs.h" - #include "libpq/libpq-fs.h" - #include "miscadmin.h" -+#include "sepgsql/hooks.h" - #include "storage/fd.h" - #include "storage/large_object.h" - #include "utils/acl.h" -@@ -172,6 +173,8 @@ lo_read(int fd, char *buf, int len) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied for large object %u", - cookies[fd]->id))); -+ /* SELinux checks */ -+ sepgsql_largeobject_read(cookies[fd]->id, cookies[fd]->snapshot); - - status = inv_read(cookies[fd], buf, len); - -@@ -204,6 +207,8 @@ lo_write(int fd, const char *buf, int len) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied for large object %u", - cookies[fd]->id))); -+ /* SELinux checks */ -+ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot); - - status = inv_write(cookies[fd], buf, len); - -@@ -233,6 +238,7 @@ Datum - lo_creat(PG_FUNCTION_ARGS) - { - Oid lobjId; -+ Oid securityId; - - /* - * We don't actually need to store into fscxt, but create it anyway to -@@ -240,7 +246,10 @@ lo_creat(PG_FUNCTION_ARGS) - */ - CreateFSContext(); - -- lobjId = inv_create(InvalidOid); -+ /* SELinux checks */ -+ securityId = sepgsql_largeobject_create(InvalidOid); -+ -+ lobjId = inv_create(InvalidOid, securityId); - - PG_RETURN_OID(lobjId); - } -@@ -249,6 +258,7 @@ Datum - lo_create(PG_FUNCTION_ARGS) - { - Oid lobjId = PG_GETARG_OID(0); -+ Oid securityId; - - /* - * We don't actually need to store into fscxt, but create it anyway to -@@ -256,7 +266,10 @@ lo_create(PG_FUNCTION_ARGS) - */ - CreateFSContext(); - -- lobjId = inv_create(lobjId); -+ /* SELinux checks */ -+ securityId = sepgsql_largeobject_create(lobjId); -+ -+ lobjId = inv_create(lobjId, securityId); - - PG_RETURN_OID(lobjId); - } -@@ -286,6 +299,9 @@ lo_unlink(PG_FUNCTION_ARGS) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be owner of large object %u", lobjId))); - -+ /* SELinux checks */ -+ sepgsql_largeobject_drop(lobjId, false); -+ - /* - * If there are any open LO FDs referencing that ID, close 'em. - */ -@@ -381,9 +397,10 @@ lo_import_internal(text *filename, Oid lobjOid) - int nbytes, - tmp; - char buf[BUFSIZE]; -- char fnamebuf[MAXPGPATH]; -+ char *fnamebuf = text_to_cstring(filename); - LargeObjectDesc *lobj; - Oid oid; -+ Oid securityId; - - #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS - if (!superuser()) -@@ -392,13 +409,14 @@ lo_import_internal(text *filename, Oid lobjOid) - errmsg("must be superuser to use server-side lo_import()"), - errhint("Anyone can use the client-side lo_import() provided by libpq."))); - #endif -- - CreateFSContext(); - -+ /* SELinux checks */ -+ securityId = sepgsql_largeobject_import(lobjOid, fnamebuf); -+ - /* - * open the file to be read in - */ -- text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); - fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666); - if (fd < 0) - ereport(ERROR, -@@ -409,7 +427,7 @@ lo_import_internal(text *filename, Oid lobjOid) - /* - * create an inversion object - */ -- oid = inv_create(lobjOid); -+ oid = inv_create(lobjOid, securityId); - - /* - * read in from the filesystem and write to the inversion object -@@ -447,7 +465,7 @@ lo_export(PG_FUNCTION_ARGS) - int nbytes, - tmp; - char buf[BUFSIZE]; -- char fnamebuf[MAXPGPATH]; -+ char *fnamebuf = text_to_cstring(filename); - LargeObjectDesc *lobj; - mode_t oumask; - -@@ -458,7 +476,6 @@ lo_export(PG_FUNCTION_ARGS) - errmsg("must be superuser to use server-side lo_export()"), - errhint("Anyone can use the client-side lo_export() provided by libpq."))); - #endif -- - CreateFSContext(); - - /* -@@ -466,6 +483,9 @@ lo_export(PG_FUNCTION_ARGS) - */ - lobj = inv_open(lobjId, INV_READ, fscxt); - -+ /* SELinux checks */ -+ sepgsql_largeobject_export(lobj->id, lobj->snapshot, fnamebuf); -+ - /* - * open the file to be written to - * -@@ -473,7 +493,6 @@ lo_export(PG_FUNCTION_ARGS) - * 022. This code used to drop it all the way to 0, but creating - * world-writable export files doesn't seem wise. - */ -- text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); - oumask = umask((mode_t) 0022); - fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666); - umask(oumask); -@@ -528,6 +547,9 @@ lo_truncate(PG_FUNCTION_ARGS) - errmsg("permission denied for large object %u", - cookies[fd]->id))); - -+ /* SELinux checks */ -+ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot); -+ - inv_truncate(cookies[fd], len); - - PG_RETURN_INT32(0); -diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c -index 0b2aa3d..90bb7d4 100644 ---- a/src/backend/nodes/copyfuncs.c -+++ b/src/backend/nodes/copyfuncs.c -@@ -1823,6 +1823,7 @@ _copyRangeTblEntry(RangeTblEntry *from) - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(modifiedCols); -+ COPY_SCALAR_FIELD(rowlvPerms); - - return newnode; - } -@@ -2755,6 +2756,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) - return newnode; - } - -+static AlterSecLabelStmt * -+_copyAlterSecLabelStmt(AlterSecLabelStmt *from) -+{ -+ AlterSecLabelStmt *newnode = makeNode(AlterSecLabelStmt); -+ -+ COPY_SCALAR_FIELD(objectType); -+ COPY_NODE_FIELD(relation); -+ COPY_NODE_FIELD(object); -+ COPY_NODE_FIELD(objarg); -+ COPY_STRING_FIELD(addname); -+ COPY_NODE_FIELD(secLabel); -+ -+ return newnode; -+} -+ - static RuleStmt * - _copyRuleStmt(RuleStmt *from) - { -@@ -3972,6 +3988,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 6e00584..2adfa91 100644 ---- a/src/backend/nodes/equalfuncs.c -+++ b/src/backend/nodes/equalfuncs.c -@@ -1309,6 +1309,19 @@ _equalAlterOwnerStmt(AlterOwnerStmt *a, AlterOwnerStmt *b) - } - - static bool -+_equalAlterSecLabelStmt(AlterSecLabelStmt *a, AlterSecLabelStmt *b) -+{ -+ COMPARE_SCALAR_FIELD(objectType); -+ COMPARE_NODE_FIELD(relation); -+ COMPARE_NODE_FIELD(object); -+ COMPARE_NODE_FIELD(objarg); -+ COMPARE_STRING_FIELD(addname); -+ COMPARE_NODE_FIELD(secLabel); -+ -+ return true; -+} -+ -+static bool - _equalRuleStmt(RuleStmt *a, RuleStmt *b) - { - COMPARE_NODE_FIELD(relation); -@@ -2188,6 +2201,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) - COMPARE_SCALAR_FIELD(checkAsUser); - COMPARE_BITMAPSET_FIELD(selectedCols); - COMPARE_BITMAPSET_FIELD(modifiedCols); -+ COMPARE_SCALAR_FIELD(rowlvPerms); - - return true; - } -@@ -2659,6 +2673,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 032150e..63f6b77 100644 ---- a/src/backend/nodes/outfuncs.c -+++ b/src/backend/nodes/outfuncs.c -@@ -2143,6 +2143,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(modifiedCols); -+ WRITE_UINT_FIELD(rowlvPerms); - } - - static void -diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c -index f28191d..f6f31e5 100644 ---- a/src/backend/nodes/readfuncs.c -+++ b/src/backend/nodes/readfuncs.c -@@ -1171,6 +1171,7 @@ _readRangeTblEntry(void) - READ_OID_FIELD(checkAsUser); - READ_BITMAPSET_FIELD(selectedCols); - READ_BITMAPSET_FIELD(modifiedCols); -+ READ_UINT_FIELD(rowlvPerms); - - READ_DONE(); - } -diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c -index 45ba902..be917ec 100644 ---- a/src/backend/optimizer/plan/planner.c -+++ b/src/backend/optimizer/plan/planner.c -@@ -39,6 +39,7 @@ - #include "parser/parse_expr.h" - #include "parser/parse_oper.h" - #include "parser/parsetree.h" -+#include "sepgsql/hooks.h" - #include "utils/lsyscache.h" - #include "utils/syscache.h" - -@@ -371,6 +372,16 @@ subquery_planner(PlannerGlobal *glob, Query *parse, - } - - /* -+ * We set up rowlvPerms before expanding inherited tables. -+ */ -+ foreach(l, parse->rtable) -+ { -+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); -+ -+ rte->rowlvPerms = sepgsql_rowlv_permissions(rte); -+ } -+ -+ /* - * Preprocess RowMark information. We need to do this after subquery - * pullup (so that all non-inherited RTEs are present) and before - * inheritance expansion (so that the info is available for -diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c -index 8902b5b..321bba9 100644 ---- a/src/backend/optimizer/util/clauses.c -+++ b/src/backend/optimizer/util/clauses.c -@@ -38,6 +38,7 @@ - #include "parser/parse_coerce.h" - #include "parser/parse_func.h" - #include "rewrite/rewriteManip.h" -+#include "sepgsql/hooks.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" - #include "utils/builtins.h" -@@ -3752,6 +3753,10 @@ inline_function(Oid funcid, Oid result_type, List *args, - if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) - return NULL; - -+ /* SELinux checks */ -+ if (!sepgsql_proc_be_inlined(func_tuple)) -+ return NULL; -+ - /* - * Make a temporary memory context, so that we don't leak all the stuff - * that parsing might create. -@@ -4205,7 +4210,8 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) - funcform->provolatile == PROVOLATILE_VOLATILE || - funcform->prosecdef || - !funcform->proretset || -- !heap_attisnull(func_tuple, Anum_pg_proc_proconfig)) -+ !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || -+ !sepgsql_proc_be_inlined(func_tuple)) - { - ReleaseSysCache(func_tuple); - return NULL; -diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c -index 70cdb59..1a88724 100644 ---- a/src/backend/parser/analyze.c -+++ b/src/backend/parser/analyze.c -@@ -25,6 +25,7 @@ - #include "postgres.h" - - #include "access/sysattr.h" -+#include "catalog/heap.h" - #include "catalog/pg_type.h" - #include "nodes/makefuncs.h" - #include "nodes/nodeFuncs.h" -@@ -40,6 +41,7 @@ - #include "parser/parse_target.h" - #include "parser/parsetree.h" - #include "rewrite/rewriteManip.h" -+#include "utils/guc.h" - #include "utils/rel.h" - - -@@ -660,7 +662,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) - tle = makeTargetEntry(expr, - attr_num, - col->name, -- false); -+ attr_num < 0 ? true : false); - qry->targetList = lappend(qry->targetList, tle); - - rte->modifiedCols = bms_add_member(rte->modifiedCols, -diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y -index 05ff082..acb1799 100644 ---- a/src/backend/parser/gram.y -+++ b/src/backend/parser/gram.y -@@ -183,8 +183,8 @@ static TypeName *TableFuncTypeName(List *columns); - %type stmt schema_stmt - AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt - AlterForeignServerStmt AlterGroupStmt -- AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt -- AlterUserStmt AlterUserMappingStmt AlterUserSetStmt -+ AlterObjectSchemaStmt AlterOwnerStmt AlterSecLabelStmt AlterSeqStmt -+ AlterTableStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt - AlterRoleStmt AlterRoleSetStmt - AlterDefaultPrivilegesStmt DefACLAction - AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt -@@ -422,6 +422,8 @@ static TypeName *TableFuncTypeName(List *columns); - %type OptTableSpace OptConsTableSpace OptTableSpaceOwner - %type opt_check_option - -+%type SecLabelItem -+ - %type xml_attribute_el - %type xml_attribute_list xml_attributes - %type xml_root_version opt_xml_root_standalone -@@ -498,7 +500,7 @@ static TypeName *TableFuncTypeName(List *columns); - - KEY - -- LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING -+ LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING - LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP - LOCATION LOCK_P LOGIN_P - -@@ -654,6 +656,7 @@ stmt : - | AlterGroupStmt - | AlterObjectSchemaStmt - | AlterOwnerStmt -+ | AlterSecLabelStmt - | AlterSeqStmt - | AlterTableStmt - | AlterRoleSetStmt -@@ -1761,6 +1764,20 @@ alter_table_cmd: - n->subtype = AT_DropOids; - $$ = (Node *)n; - } -+ /* ALTER TABLE SET WITH SECURITY LABEL */ -+ | SET WITH SECURITY LABEL -+ { -+ AlterTableCmd *n = makeNode(AlterTableCmd); -+ n->subtype = AT_AddSecLabel; -+ $$ = (Node *)n; -+ } -+ /* ALTER TABLE SET WITHOUT SECURITY LABEL */ -+ | SET WITHOUT SECURITY LABEL -+ { -+ AlterTableCmd *n = makeNode(AlterTableCmd); -+ n->subtype = AT_DropSecLabel; -+ $$ = (Node *)n; -+ } - /* ALTER TABLE CLUSTER ON */ - | CLUSTER ON name - { -@@ -6028,6 +6045,102 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId - } - ; - -+/***************************************************************************** -+ * -+ * ALTER THING name SECURITY LABEL TO new_label -+ * -+ *****************************************************************************/ -+ -+AlterSecLabelStmt: ALTER DATABASE database_name SecLabelItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_DATABASE; -+ n->object = list_make1(makeString($3)); -+ n->secLabel = $4; -+ $$ = (Node *)n; -+ } -+ | ALTER SCHEMA name SecLabelItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_SCHEMA; -+ n->object = list_make1(makeString($3)); -+ n->secLabel = $4; -+ $$ = (Node *)n; -+ } -+ | ALTER TABLE relation_expr SecLabelItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_TABLE; -+ n->relation = $3; -+ n->secLabel = $4; -+ $$ = (Node *)n; -+ } -+ | ALTER TABLE relation_expr ALTER opt_column ColId SecLabelItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_COLUMN; -+ n->relation = $3; -+ n->addname = $6; -+ n->secLabel = $7; -+ $$ = (Node *)n; -+ } -+ | ALTER SEQUENCE qualified_name SecLabelItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_SEQUENCE; -+ n->relation = $3; -+ n->secLabel = $4; -+ $$ = (Node *)n; -+ } -+ | ALTER VIEW qualified_name SecLabelItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_VIEW; -+ n->relation = $3; -+ n->secLabel = $4; -+ $$ = (Node *)n; -+ } -+ | ALTER FUNCTION function_with_argtypes SecLabelItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_FUNCTION; -+ n->object = $3->funcname; -+ n->objarg = $3->funcargs; -+ n->secLabel = $4; -+ $$ = (Node *)n; -+ } -+ | ALTER AGGREGATE func_name aggr_args SecLabelItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_AGGREGATE; -+ n->object = $3; -+ n->objarg = $4; -+ n->secLabel = $5; -+ $$ = (Node *)n; -+ } -+ | ALTER LARGE_P OBJECT_P Iconst SecLabelItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_LARGEOBJECT; -+ n->object = list_make1(makeInteger($4)); -+ n->secLabel = $5; -+ $$ = (Node *)n; -+ } -+ | ALTER TYPE_P any_name SecLabelItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_TYPE; -+ n->object = $3; -+ n->secLabel = $4; -+ $$ = (Node *)n; -+ } -+ ; -+ -+SecLabelItem: SECURITY LABEL TO Sconst -+ { -+ $$ = makeString($4); -+ } -+ ; - - /***************************************************************************** - * -@@ -10922,6 +11035,7 @@ unreserved_keyword: - | INVOKER - | ISOLATION - | KEY -+ | LABEL - | LANGUAGE - | LARGE_P - | LAST_P -diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c -index 1d0fc82..7ad1eeb 100644 ---- a/src/backend/parser/parse_relation.c -+++ b/src/backend/parser/parse_relation.c -@@ -2302,8 +2302,8 @@ specialAttNum(const char *attname) - { - Form_pg_attribute sysatt; - -- sysatt = SystemAttributeByName(attname, -- true /* "oid" will be accepted */ ); -+ /* "oid" and "security_label" will be accepted */ -+ sysatt = SystemAttributeByName(attname, true, true); - if (sysatt != NULL) - return sysatt->attnum; - return InvalidAttrNumber; -@@ -2324,7 +2324,9 @@ attnumAttName(Relation rd, int attid) - { - Form_pg_attribute sysatt; - -- sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids); -+ sysatt = SystemAttributeDefinition(attid, -+ rd->rd_rel->relhasoids, -+ rd->rd_rel->relhassecids); - return &sysatt->attname; - } - if (attid > rd->rd_att->natts) -@@ -2346,7 +2348,9 @@ attnumTypeId(Relation rd, int attid) - { - Form_pg_attribute sysatt; - -- sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids); -+ sysatt = SystemAttributeDefinition(attid, -+ rd->rd_rel->relhasoids, -+ rd->rd_rel->relhassecids); - return sysatt->atttypid; - } - if (attid > rd->rd_att->natts) -diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c -index 7e04a94..da10dc7 100644 ---- a/src/backend/parser/parse_target.c -+++ b/src/backend/parser/parse_target.c -@@ -14,6 +14,7 @@ - */ - #include "postgres.h" - -+#include "catalog/heap.h" - #include "catalog/pg_type.h" - #include "commands/dbcommands.h" - #include "funcapi.h" -@@ -365,16 +366,34 @@ transformAssignedExpr(ParseState *pstate, - Oid attrtype; /* type of target column */ - int32 attrtypmod; - Relation rd = pstate->p_target_relation; -+ bool relhasoids = RelationGetForm(rd)->relhasoids; -+ bool relhassecids = RelationGetForm(rd)->relhassecids; - - Assert(rd != NULL); -- if (attrno <= 0) -- ereport(ERROR, -- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -- errmsg("cannot assign to system column \"%s\"", -- colname), -- parser_errposition(pstate, location))); -- attrtype = attnumTypeId(rd, attrno); -- attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; -+ if (attrno > 0) -+ { -+ attrtype = attnumTypeId(rd, attrno); -+ attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; -+ } -+ else -+ { -+ Form_pg_attribute attr; -+ -+ attr = SystemAttributeDefinition(attrno, relhasoids, relhassecids); -+ if (attr && SystemAttributeWritable(attrno, relhasoids, relhassecids)) -+ { -+ attrtype = attr->atttypid; -+ attrtypmod = attr->atttypmod; -+ } -+ else -+ { -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("cannot assign to system column \"%s\"", colname), -+ parser_errposition(pstate, location))); -+ return NULL; -+ } -+ } - - /* - * If the expression is a DEFAULT placeholder, insert the attribute's -@@ -503,6 +522,10 @@ updateTargetListEntry(ParseState *pstate, - List *indirection, - int location) - { -+ Relation rel = pstate->p_target_relation; -+ bool relhasoids = RelationGetForm(rel)->relhasoids; -+ bool relhassecids = RelationGetForm(rel)->relhassecids; -+ - /* Fix up expression as needed */ - tle->expr = transformAssignedExpr(pstate, - tle->expr, -@@ -519,6 +542,9 @@ updateTargetListEntry(ParseState *pstate, - */ - tle->resno = (AttrNumber) attrno; - tle->resname = colname; -+ -+ if (SystemAttributeWritable(attrno, relhasoids, relhassecids)) -+ tle->resjunk = true; - } - - -@@ -793,6 +819,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) - Bitmapset *wholecols = NULL; - Bitmapset *partialcols = NULL; - ListCell *tl; -+ uint32 system_attrs = 0UL; - - foreach(tl, cols) - { -@@ -801,14 +828,42 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) - int attrno; - - /* Lookup column name, ereport on failure */ -- attrno = attnameAttNum(pstate->p_target_relation, name, false); -+ attrno = attnameAttNum(pstate->p_target_relation, name, true); - if (attrno == InvalidAttrNumber) -+ { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - name, - RelationGetRelationName(pstate->p_target_relation)), - parser_errposition(pstate, col->location))); -+ } -+ else if (attrno < 0) -+ { -+ Relation rel = pstate->p_target_relation; -+ bool relhasoids = RelationGetForm(rel)->relhasoids; -+ bool relhassecids = RelationGetForm(rel)->relhassecids; -+ -+ if (SystemAttributeWritable(attrno, relhasoids, relhassecids)) -+ { -+ uint32 mask = (1<<(-attrno)); -+ -+ if ((system_attrs & mask) != 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_DUPLICATE_COLUMN), -+ errmsg("column \"%s\" specified more than once", -+ name), -+ parser_errposition(pstate, col->location))); -+ system_attrs |= mask; -+ *attrnos = lappend_int(*attrnos, attrno); -+ continue; -+ } -+ ereport(ERROR, -+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), -+ errmsg("column \"%s\" of relation \"%s\" is system column", -+ name, RelationGetRelationName(rel)), -+ parser_errposition(pstate, col->location))); -+ } - - /* - * Check for duplicates, but only of whole columns --- we allow -@@ -1263,7 +1318,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) - expandRTE(rte, var->varno, 0, var->location, false, - &names, &vars); - -- tupleDesc = CreateTemplateTupleDesc(list_length(vars), false); -+ tupleDesc = CreateTemplateTupleDesc(list_length(vars), false, false); - i = 1; - forboth(lname, names, lvar, vars) - { -diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c -index d1e1dac..8e994da 100644 ---- a/src/backend/parser/parse_utilcmd.c -+++ b/src/backend/parser/parse_utilcmd.c -@@ -53,8 +53,10 @@ - #include "parser/parse_utilcmd.h" - #include "parser/parser.h" - #include "rewrite/rewriteManip.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" -+#include "utils/guc.h" - #include "utils/lsyscache.h" - #include "utils/relcache.h" - #include "utils/syscache.h" -@@ -70,6 +72,7 @@ typedef struct - List *inhRelations; /* relations to inherit from */ - bool isalter; /* true if altering existing table */ - bool hasoids; /* does relation have an OID column? */ -+ bool hassecids; /* does relation have an security label? */ - List *columns; /* ColumnDef items */ - List *ckconstraints; /* CHECK constraints */ - List *fkconstraints; /* FOREIGN KEY constraints */ -@@ -185,6 +188,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) - cxt.alist = NIL; - cxt.pkey = NULL; - cxt.hasoids = interpretOidsOption(stmt->options); -+ cxt.hassecids = default_with_secids; - - Assert(!stmt->ofTypename || !stmt->inhRelations); /* grammar enforces */ - -@@ -599,6 +603,9 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(relation)); - -+ /* SELinux checks */ -+ sepgsql_relation_getattr(RelationGetRelid(relation)); -+ - tupleDesc = RelationGetDescr(relation); - constr = tupleDesc->constr; - -@@ -1380,7 +1387,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) - if (constraint->contype == CONSTR_PRIMARY) - column->is_not_null = TRUE; - } -- else if (SystemAttributeByName(key, cxt->hasoids) != NULL) -+ else if (SystemAttributeByName(key, cxt->hasoids, cxt->hassecids) != NULL) - { - /* - * column will be a system column in the new table, so accept it. -@@ -1957,6 +1964,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) - cxt.inhRelations = NIL; - cxt.isalter = true; - cxt.hasoids = false; /* need not be right */ -+ cxt.hassecids = false; /* need not be right */ - cxt.columns = NIL; - cxt.ckconstraints = NIL; - cxt.fkconstraints = NIL; -diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c -index 279f0f0..f91f746 100644 ---- a/src/backend/postmaster/postmaster.c -+++ b/src/backend/postmaster/postmaster.c -@@ -109,6 +109,7 @@ - #include "postmaster/postmaster.h" - #include "postmaster/syslogger.h" - #include "replication/walsender.h" -+#include "sepgsql/hooks.h" - #include "storage/fd.h" - #include "storage/ipc.h" - #include "storage/pg_shmem.h" -@@ -212,7 +213,8 @@ static pid_t StartupPID = 0, - AutoVacPID = 0, - PgArchPID = 0, - PgStatPID = 0, -- SysLoggerPID = 0; -+ SysLoggerPID = 0, -+ SecWorkerPID = 0; - - /* Startup/shutdown state */ - #define NoShutdown 0 -@@ -467,6 +469,7 @@ static void ShmemBackendArrayRemove(Backend *bn); - #define StartBackgroundWriter() StartChildProcess(BgWriterProcess) - #define StartWalWriter() StartChildProcess(WalWriterProcess) - #define StartWalReceiver() StartChildProcess(WalReceiverProcess) -+#define StartSecurityWorker() StartChildProcess(SecurityWorkerProcess) - - /* Macros to check exit status of a child process */ - #define EXIT_STATUS_0(st) ((st) == 0) -@@ -1480,6 +1483,11 @@ ServerLoop(void) - if (PgStatPID == 0 && pmState == PM_RUN) - PgStatPID = pgstat_start(); - -+ /* If we have lost security worker, try to start a new one */ -+ if (SecWorkerPID == 0 && pmState == PM_RUN && -+ sepgsql_worker_needed()) -+ SecWorkerPID = StartSecurityWorker(); -+ - /* If we need to signal the autovacuum launcher, do so now */ - if (avlauncher_needs_signal) - { -@@ -2125,6 +2133,8 @@ SIGHUP_handler(SIGNAL_ARGS) - signal_child(SysLoggerPID, SIGHUP); - if (PgStatPID != 0) - signal_child(PgStatPID, SIGHUP); -+ if (SecWorkerPID != 0) -+ signal_child(SecWorkerPID, SIGHUP); - - /* Reload authentication config files too */ - if (!load_hba()) -@@ -2185,6 +2195,9 @@ pmdie(SIGNAL_ARGS) - /* and the walwriter too */ - if (WalWriterPID != 0) - signal_child(WalWriterPID, SIGTERM); -+ /* and the security worker too */ -+ if (SecWorkerPID != 0) -+ signal_child(SecWorkerPID, SIGTERM); - - /* - * If we're in recovery, we can't kill the startup process -@@ -2247,6 +2260,9 @@ pmdie(SIGNAL_ARGS) - /* and the walwriter too */ - if (WalWriterPID != 0) - signal_child(WalWriterPID, SIGTERM); -+ /* and the security worker too */ -+ if (SecWorkerPID != 0) -+ signal_child(SecWorkerPID, SIGTERM); - pmState = PM_WAIT_BACKENDS; - } - -@@ -2282,6 +2298,8 @@ pmdie(SIGNAL_ARGS) - signal_child(PgArchPID, SIGQUIT); - if (PgStatPID != 0) - signal_child(PgStatPID, SIGQUIT); -+ if (SecWorkerPID != 0) -+ signal_child(SecWorkerPID, SIGQUIT); - ExitPostmaster(0); - break; - } -@@ -2554,6 +2572,16 @@ reaper(SIGNAL_ARGS) - continue; - } - -+ /* Was it the security worker process? */ -+ if (pid == SecWorkerPID) -+ { -+ SecWorkerPID = 0; -+ if (!EXIT_STATUS_0(exitstatus)) -+ LogChildExit(LOG, _("security worker process"), -+ pid, exitstatus); -+ continue; -+ } -+ - /* - * Else do standard backend child cleanup. - */ -@@ -2770,6 +2798,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) - signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); - } - -+ /* Take care of the security worker process too */ -+ if (pid == SecWorkerPID) -+ SecWorkerPID = 0; -+ else if (SecWorkerPID != 0 && !FatalError) -+ { -+ ereport(DEBUG2, -+ (errmsg_internal("sending %s to process %d", -+ (SendStop ? "SIGSTOP" : "SIGQUIT"), -+ (int) SecWorkerPID))); -+ signal_child(SecWorkerPID, (SendStop ? SIGSTOP : SIGQUIT)); -+ } -+ - /* - * Force a power-cycle of the pgarch process too. (This isn't absolutely - * necessary, but it seems like a good idea for robustness, and it -@@ -2926,7 +2966,8 @@ PostmasterStateMachine(void) - WalReceiverPID == 0 && - (BgWriterPID == 0 || !FatalError) && - WalWriterPID == 0 && -- AutoVacPID == 0) -+ AutoVacPID == 0 && -+ SecWorkerPID == 0) - { - if (FatalError) - { -diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c -index 06469b3..34f8cbe 100644 ---- a/src/backend/rewrite/rewriteDefine.c -+++ b/src/backend/rewrite/rewriteDefine.c -@@ -27,6 +27,7 @@ - #include "rewrite/rewriteDefine.h" - #include "rewrite/rewriteManip.h" - #include "rewrite/rewriteSupport.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/inval.h" -@@ -265,6 +266,9 @@ DefineQueryRewrite(char *rulename, - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(event_relation)); - -+ /* SELinux checks */ -+ sepgsql_rule_create(event_relid, rulename); -+ - /* - * No rule actions that modify OLD or NEW - */ -diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c -index 2bbfc1f..b412ee9 100644 ---- a/src/backend/rewrite/rewriteRemove.c -+++ b/src/backend/rewrite/rewriteRemove.c -@@ -22,6 +22,7 @@ - #include "catalog/pg_rewrite.h" - #include "miscadmin.h" - #include "rewrite/rewriteRemove.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/fmgroids.h" - #include "utils/inval.h" -@@ -77,6 +78,9 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - get_rel_name(eventRelationOid)); - -+ /* SELinux checks */ -+ sepgsql_rule_drop(eventRelationOid, ruleName, false); -+ - /* - * Do the deletion - */ -diff --git a/src/backend/sepgsql/Makefile b/src/backend/sepgsql/Makefile -new file mode 100644 -index 0000000..c8fb740 ---- /dev/null -+++ b/src/backend/sepgsql/Makefile -@@ -0,0 +1,21 @@ -+# -+# Makefile for security subsystem -+# -+ -+subdir = src/backend/sepgsql -+top_builddir = ../../.. -+include $(top_builddir)/src/Makefile.global -+ -+ifeq ($(enable_selinux), yes) -+OBJS = selinux.o avc.o label.o -+else -+OBJS = dummy.o -+endif -+ -+OBJS += rowlv.o -+ -+OBJS += misc.o database.o schema.o relation.o attribute.o proc.o \ -+ type.o tablespace.o operator.o role.o blob.o conversion.o \ -+ tsearch.o fdw.o file.o -+ -+include $(top_srcdir)/src/backend/common.mk -diff --git a/src/backend/sepgsql/attribute.c b/src/backend/sepgsql/attribute.c -new file mode 100644 -index 0000000..3b9c916 ---- /dev/null -+++ b/src/backend/sepgsql/attribute.c -@@ -0,0 +1,260 @@ -+/* -+ * attribute.c -+ * -+ * SELinux hooks related to attribute -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "access/heapam.h" -+#include "access/sysattr.h" -+#include "catalog/pg_attribute.h" -+#include "catalog/pg_class.h" -+#include "catalog/pg_seclabel.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/lsyscache.h" -+#include "utils/syscache.h" -+#include "utils/tqual.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_attribute_common(Oid relOid, AttrNumber attno, -+ uint32 required, bool abort) -+{ -+ Form_pg_attribute attForm; -+ HeapTuple tuple; -+ sepgsql_sid_t tsid; -+ char auname[NAMEDATALEN * 2 + 10]; -+ bool retval; -+ -+ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); -+ -+ tuple = SearchSysCache2(ATTNUM, -+ ObjectIdGetDatum(relOid), -+ Int16GetDatum(attno)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for attribute %d of relation %u", -+ attno, relOid); -+ attForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ -+ tsid.relid = AttributeRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ snprintf(auname, sizeof(auname), "%s.%s", -+ get_rel_name(relOid), NameStr(attForm->attname)); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_COLUMN, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+#endif -+ -+Oid -+sepgsql_attribute_create(Oid relOid, const char *attName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ char auname[NAMEDATALEN * 2 + 10]; -+ -+ if (get_rel_relkind(relOid) != RELKIND_RELATION) -+ { -+ sepgsql_relation_common(relOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ nsid.relid = RelationRelationId; -+ nsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); -+ -+ nsid = sepgsql_move_secid(AttributeRelationId, nsid); -+ -+ return nsid.secid; -+ } -+ -+ nsid = sepgsql_get_default_column_secid(relOid); -+ snprintf(auname, sizeof(auname), "%s.%s", -+ get_rel_name(relOid), attName); -+ -+ /* db_column:{create} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_COLUMN, -+ SEPG_DB_COLUMN__CREATE, -+ auname, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_attribute_alter(Oid relOid, const char *attName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ AttrNumber attnum = get_attnum(relOid, attName); -+ -+ if (attnum == InvalidAttrNumber) -+ return; /* to be failed later */ -+ -+ if (get_rel_relkind(relOid) == RELKIND_RELATION) -+ sepgsql_attribute_common(relOid, attnum, -+ SEPG_DB_COLUMN__SETATTR, true); -+ else -+ sepgsql_relation_common(relOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_attribute_relabel(Oid relOid, const char *attName, char *new_label) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ AttrNumber attnum = get_attnum(relOid, attName); -+ sepgsql_sid_t nsid; -+ char auname[NAMEDATALEN * 2 + 10]; -+ -+ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); -+ if (attnum == InvalidAttrNumber) -+ return InvalidOid; /* to be failed later */ -+ -+ nsid.relid = AttributeRelationId; -+ nsid.secid = seclabelTransInput(nsid.relid, new_label); -+ -+ snprintf(auname, sizeof(auname), "%s.%s", -+ get_rel_name(relOid), get_attname(relOid, attnum)); -+ -+ /* db_column:{setattr relabelfrom} */ -+ sepgsql_attribute_common(relOid, attnum, -+ SEPG_DB_COLUMN__SETATTR | -+ SEPG_DB_COLUMN__RELABELFROM, -+ true); -+ -+ /* db_column:{relabelto} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_COLUMN, -+ SEPG_DB_COLUMN__RELABELTO, -+ auname, -+ true); -+ return nsid.secid; -+ } -+#endif -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL is not available"))); -+ return InvalidOid; -+} -+ -+void -+sepgsql_attribute_drop(Oid relOid, const char *attName, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ AttrNumber attnum = get_attnum(relOid, attName); -+ -+ /* -+ * If not found, the caller raises an error with an appropriate -+ * error message. -+ */ -+ if (attnum == InvalidAttrNumber) -+ return; -+ -+ if (get_rel_relkind(relOid) == RELKIND_RELATION) -+ { -+ sepgsql_attribute_common(relOid, attnum, -+ SEPG_DB_COLUMN__DROP, true); -+ /* -+ * ALTER TABLE SET WITHOUT SECURITY LABEL is equivalent to -+ * relabel all the tuples within the target relation. -+ * In this case, we need to check {relabelfrom relabelto} -+ */ -+ if (!cascade && -+ attnum == SecurityLabelAttributeNumber) -+ { -+ Relation rel; -+ HeapScanDesc scan; -+ HeapTuple tuple; -+ sepgsql_sid_t tsid; -+ -+ rel = heap_open(relOid, AccessShareLock); -+ -+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); -+ -+ while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) -+ { -+ /* db_tuple:{update relabelfrom} */ -+ tsid.relid = relOid; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__UPDATE | -+ SEPG_DB_TUPLE__RELABELFROM, -+ NULL, -+ true); -+ } -+ heap_endscan(scan); -+ -+ heap_close(rel, AccessShareLock); -+ -+ /* db_tuple:{relabelto} */ -+ tsid.relid = RelationRelationId; -+ tsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); -+ -+ sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__RELABELTO, -+ NULL, -+ true); -+ } -+ } -+ else if (!cascade) -+ sepgsql_relation_common(relOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_attribute_grant(Oid relOid, AttrNumber attnum) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ if (get_rel_relkind(relOid) == RELKIND_RELATION) -+ sepgsql_attribute_common(relOid, attnum, -+ SEPG_DB_COLUMN__SETATTR, true); -+ else -+ sepgsql_relation_common(relOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_attribute_comment(Oid relOid, AttrNumber attnum) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ if (get_rel_relkind(relOid) == RELKIND_RELATION) -+ sepgsql_attribute_common(relOid, attnum, -+ SEPG_DB_COLUMN__SETATTR, true); -+ else -+ sepgsql_relation_common(relOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/avc.c b/src/backend/sepgsql/avc.c -new file mode 100644 -index 0000000..5f58974 ---- /dev/null -+++ b/src/backend/sepgsql/avc.c -@@ -0,0 +1,503 @@ -+/* -+ * avc.c -+ * userspace access vector cache -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "access/hash.h" -+#include "access/xact.h" -+#include "catalog/pg_seclabel.h" -+#include "libpq/libpq-be.h" -+#include "libpq/pqsignal.h" -+#include "miscadmin.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "storage/shmem.h" -+#include "storage/lock.h" -+#include "utils/memutils.h" -+ -+#include -+#include -+#include -+ -+/* ------------------------------------------------------------ -+ * -+ * Userspace Access Vector Cache -+ * -+ * ------------------------------------------------------------ -+ */ -+static MemoryContext AvcMemCtx = NULL; -+ -+#define AVC_HASH_NUM_SLOTS 256 -+#define AVC_HASH_NUM_NODES 180 -+ -+#define avc_hash_key(trelid, tsecid, tclass, nrelid) \ -+ (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3) ^ (nrelid))) -+ -+typedef struct _avc_datum -+{ -+ uint32 hash_key; -+ -+ uint16 tclass; -+ sepgsql_sid_t tsid; -+ sepgsql_sid_t nsid; -+ char *tcontext; -+ char *ncontext; -+ -+ uint32 allowed; -+ uint32 auditallow; -+ uint32 auditdeny; -+ bool permissive; -+ -+ bool hot_cache; -+} avc_datum; -+ -+typedef struct _avc_page -+{ -+ struct _avc_page *next; -+ -+ List *slot[AVC_HASH_NUM_SLOTS]; -+ -+ uint32 avc_count; -+ uint32 lru_hint; -+ -+ char scontext[1]; -+} avc_page; -+ -+static avc_page *current_page = NULL; -+ -+static int avc_version = -1; -+ -+/* -+ * selinux_state -+ * -+ * It is deployed on the shared memory region, to show the system -+ * state of SELinux and its security policy. -+ */ -+struct -+{ -+ int version; -+ -+ bool enforcing; -+} *selinux_state = NULL; -+ -+Size -+sepgsql_shmem_size(void) -+{ -+ return sizeof(*selinux_state); -+} -+ -+static void -+sepgsql_shmem_init(void) -+{ -+ bool found; -+ -+ selinux_state = ShmemInitStruct("SELinux system state", -+ sepgsql_shmem_size(), &found); -+ if (!found) -+ { -+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); -+ -+ selinux_state->version = 0; -+ selinux_state->enforcing = (security_getenforce() > 0); -+ -+ LWLockRelease(SepgsqlAvcLock); -+ } -+} -+ -+void -+sepgsql_avc_switch(const char *scontext) -+{ -+ avc_page *new_page; -+ int i, length; -+ -+ if (current_page) -+ { -+ new_page = current_page; -+ do { -+ if (strcmp(new_page->scontext, scontext) == 0) -+ { -+ current_page = new_page; -+ return; -+ } -+ new_page = new_page->next; -+ } while (new_page != current_page); -+ } -+ -+ /* -+ * Not found, so create a new avc_page -+ */ -+ length = sizeof(avc_page) + strlen(scontext); -+ new_page = MemoryContextAllocZero(AvcMemCtx, length); -+ -+ strcpy(new_page->scontext, scontext); -+ for (i = 0; i < AVC_HASH_NUM_SLOTS; i++) -+ new_page->slot[i] = NIL; -+ -+ if (!current_page) -+ new_page->next = new_page; -+ else -+ { -+ new_page->next = current_page->next; -+ current_page->next = new_page; -+ } -+ current_page = new_page; -+} -+ -+ -+static void -+sepgsql_avc_reset(void) -+{ -+ Assert(AvcMemCtx != NULL); -+ -+ MemoryContextReset(AvcMemCtx); -+ -+ current_page = NULL; -+ -+ sepgsql_avc_switch(sepgsql_get_client_label()); -+} -+ -+static bool -+sepgsql_avc_is_valid(void) -+{ -+ bool result = true; -+ -+ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); -+ if (avc_version != selinux_state->version) -+ { -+ sepgsql_avc_reset(); -+ -+ /* copy current version to local variable */ -+ avc_version = selinux_state->version; -+ -+ result = false; -+ } -+ LWLockRelease(SepgsqlAvcLock); -+ -+ return result; -+} -+ -+ -+static void -+sepgsql_avc_reclaim(avc_page *page) -+{ -+ ListCell *l; -+ -+ while (page->avc_count > AVC_HASH_NUM_NODES - 10) -+ { -+ foreach (l, page->slot[page->lru_hint]) -+ { -+ avc_datum *cache = lfirst(l); -+ -+ if (cache->hot_cache) -+ cache->hot_cache = false; -+ { -+ list_delete_ptr(page->slot[page->lru_hint], cache); -+ pfree(cache); -+ page->avc_count--; -+ } -+ } -+ page->lru_hint = (page->lru_hint + 1) % AVC_HASH_NUM_SLOTS; -+ } -+} -+ -+static avc_datum * -+sepgsql_avc_make_entry(avc_page *page, -+ sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) -+{ -+ struct av_decision avd; -+ MemoryContext oldctx; -+ char *scontext; -+ char *tcontext; -+ char *ncontext; -+ avc_datum *cache; -+ uint32 hash_key, index; -+ -+ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid); -+ index = hash_key % AVC_HASH_NUM_SLOTS; -+ -+ oldctx = MemoryContextSwitchTo(AvcMemCtx); -+ -+ scontext = page->scontext; -+ tcontext = seclabelRawOutput(tsid.relid, tsid.secid); -+ ncontext = sepgsql_compute_create(scontext, tcontext, tclass); -+ -+ sepgsql_compute_avd(scontext, tcontext, tclass, &avd); -+ -+ cache = palloc0(sizeof(avc_datum)); -+ -+ cache->hash_key = hash_key; -+ -+ cache->tclass = tclass; -+ -+ cache->hot_cache = true; -+ cache->tcontext = tcontext; -+ cache->ncontext = ncontext; -+ -+ cache->tsid.relid = tsid.relid; -+ cache->tsid.secid = tsid.secid; -+ -+ if (OidIsValid(nrelid)) -+ { -+ cache->nsid.relid = nrelid; -+ cache->nsid.secid = seclabelRawInput(nrelid, ncontext); -+ } -+ -+ cache->allowed = avd.allowed; -+ cache->auditallow = avd.auditallow; -+ cache->auditdeny = avd.auditdeny; -+ if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) -+ cache->permissive = true; -+ -+ if (page->avc_count > AVC_HASH_NUM_NODES) -+ sepgsql_avc_reclaim(page); -+ -+ page->slot[index] = lcons(cache, page->slot[index]); -+ page->avc_count++; -+ -+ MemoryContextSwitchTo(oldctx); -+ -+ return cache; -+} -+ -+static avc_datum * -+sepgsql_avc_lookup(avc_page *page, -+ sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) -+{ -+ avc_datum *cache = NULL; -+ uint32 hash_key, index; -+ ListCell *l; -+ -+ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid); -+ index = hash_key % AVC_HASH_NUM_SLOTS; -+ -+ foreach (l, page->slot[index]) -+ { -+ cache = lfirst(l); -+ if (cache->hash_key == hash_key && -+ cache->tclass == tclass && -+ cache->tsid.relid == tsid.relid && -+ cache->tsid.secid == tsid.secid && -+ cache->nsid.relid == nrelid) -+ { -+ cache->hot_cache = true; -+ return cache; -+ } -+ } -+ return NULL; -+} -+ -+bool -+sepgsql_client_perms(sepgsql_sid_t tsid, -+ uint16 tclass, uint32 required, -+ const char *audit_name, bool abort) -+{ -+ avc_datum *cache; -+ uint32 denied, audited; -+ bool result = true; -+ -+ do { -+ cache = sepgsql_avc_lookup(current_page, -+ tsid, tclass, InvalidOid); -+ if (!cache) -+ cache = sepgsql_avc_make_entry(current_page, -+ tsid, tclass, InvalidOid); -+ } while (!sepgsql_avc_is_valid()); -+ -+ denied = required & ~cache->allowed; -+ if (sepgsql_debug_audit && tclass != SEPG_CLASS_DB_TUPLE) -+ audited = (denied ? (denied & ~0) : (required & ~0)); -+ else -+ audited = (denied ? (denied & cache->auditdeny) -+ : (required & cache->auditallow)); -+ -+ if (audited) -+ { -+ sepgsql_audit_log(!!denied, -+ current_page->scontext, -+ seclabelRawOutput(tsid.relid, tsid.secid), -+ tclass, audited, audit_name); -+ } -+ -+ if (denied) -+ { -+ if (!sepgsql_get_enforce() || cache->permissive) -+ cache->allowed |= required; /* prevent flood of audit log */ -+ else -+ { -+ if (abort) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("SELinux: security policy violation"))); -+ result = false; -+ } -+ } -+ -+ return result; -+} -+ -+sepgsql_sid_t -+sepgsql_client_create_secid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) -+{ -+ avc_datum *cache; -+ -+ do { -+ cache = sepgsql_avc_lookup(current_page, tsid, tclass, nrelid); -+ -+ if (!cache) -+ cache = sepgsql_avc_make_entry(current_page, -+ tsid, tclass, nrelid); -+ } while (!sepgsql_avc_is_valid()); -+ -+ return cache->nsid; -+} -+ -+char * -+sepgsql_client_create_label(sepgsql_sid_t tsid, uint16 tclass) -+{ -+ avc_datum *cache; -+ -+ do { -+ cache = sepgsql_avc_lookup(current_page, tsid, tclass, InvalidOid); -+ -+ if (!cache) -+ cache = sepgsql_avc_make_entry(current_page, -+ tsid, tclass, InvalidOid); -+ } while (!sepgsql_avc_is_valid()); -+ -+ return cache->ncontext; -+} -+ -+static void -+sepgsql_avc_xact_callback(XactEvent event, void *arg) -+{ -+ if (event == XACT_EVENT_ABORT) -+ sepgsql_avc_reset(); -+} -+ -+static void -+sepgsql_avc_sub_xact_callback(SubXactEvent event, SubTransactionId mySubid, -+ SubTransactionId parentSubid, void *arg) -+{ -+ if (event == SUBXACT_EVENT_ABORT_SUB) -+ sepgsql_avc_reset(); -+} -+ -+void -+sepgsql_avc_init(void) -+{ -+ sepgsql_shmem_init(); -+ -+ AvcMemCtx = AllocSetContextCreate(TopMemoryContext, -+ "Userspace AVC", -+ ALLOCSET_DEFAULT_MINSIZE, -+ ALLOCSET_DEFAULT_INITSIZE, -+ ALLOCSET_DEFAULT_MAXSIZE); -+ /* -+ * userspace avc should be invalidate when the current transaction -+ * is aborted on errors, because sid to be created shall be rollbacked. -+ */ -+ RegisterXactCallback(sepgsql_avc_xact_callback, NULL); -+ RegisterSubXactCallback(sepgsql_avc_sub_xact_callback, NULL); -+} -+ -+/* ------------------------------------------------------------ -+ * -+ * SELinux state monitor process -+ * -+ * ------------------------------------------------------------ -+ */ -+static int -+sepgsql_cb_log(int type, const char *fmt, ...) -+{ -+ char *c, buffer[1024]; -+ va_list ap; -+ -+ va_start(ap, fmt); -+ vsnprintf(buffer, sizeof(buffer), fmt, ap); -+ va_end(ap); -+ -+ c = strrchr(buffer, '\n'); -+ if (c) -+ *c = '\0'; -+ -+ ereport(LOG,(errmsg("%s", buffer))); -+ -+ return 0; -+} -+ -+static int -+sepgsql_cb_setenforce(int enforce) -+{ -+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); -+ selinux_state->enforcing = (enforce ? true : false); -+ selinux_state->version++; -+ LWLockRelease(SepgsqlAvcLock); -+ -+ return 0; -+} -+ -+static int -+sepgsql_cb_policyload(int seqno) -+{ -+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); -+ selinux_state->version++; -+ LWLockRelease(SepgsqlAvcLock); -+ -+ return 0; -+} -+ -+void -+sepgsql_avc_worker_main(void) -+{ -+ union selinux_callback cb; -+ -+ Assert(sepgsql_is_enabled()); -+ -+#ifdef HAVE_SETSID -+ if (setsid() < 0) -+ elog(FATAL, "setsid() failed: %m"); -+#endif -+ -+ /* -+ * setup the signal handler -+ */ -+ pqinitmask(); -+ pqsignal(SIGHUP, SIG_IGN); -+ pqsignal(SIGINT, SIG_IGN); -+ pqsignal(SIGTERM, exit); -+ pqsignal(SIGQUIT, exit); -+ pqsignal(SIGUSR1, SIG_IGN); -+ pqsignal(SIGUSR2, SIG_IGN); -+ pqsignal(SIGCHLD, SIG_DFL); -+ PG_SETMASK(&UnBlockSig); -+ -+ /* -+ * map shared memory segment -+ */ -+ sepgsql_shmem_init(); -+ -+ ereport(LOG, (errmsg("SELinux: netlink receiver (pid=%u)", getpid()))); -+ -+ /* -+ * setup callback functions from avc_netlink_loop() -+ */ -+ cb.func_log = sepgsql_cb_log; -+ selinux_set_callback(SELINUX_CB_LOG, cb); -+ cb.func_setenforce = sepgsql_cb_setenforce; -+ selinux_set_callback(SELINUX_CB_SETENFORCE, cb); -+ cb.func_policyload = sepgsql_cb_policyload; -+ selinux_set_callback(SELINUX_CB_POLICYLOAD, cb); -+ -+ /* -+ * open netlink socket and wait for messages -+ */ -+ avc_netlink_open(1); -+ -+ avc_netlink_loop(); -+ -+ exit(0); -+} -diff --git a/src/backend/sepgsql/blob.c b/src/backend/sepgsql/blob.c -new file mode 100644 -index 0000000..e60e9cc ---- /dev/null -+++ b/src/backend/sepgsql/blob.c -@@ -0,0 +1,245 @@ -+/* -+ * blob.c -+ * -+ * SELinux hooks related to large objects -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "access/genam.h" -+#include "access/heapam.h" -+#include "access/sysattr.h" -+#include "catalog/indexing.h" -+#include "catalog/pg_largeobject.h" -+#include "catalog/pg_largeobject_metadata.h" -+#include "catalog/pg_seclabel.h" -+#include "miscadmin.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/fmgroids.h" -+#include "utils/tqual.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_largeobject_common(Oid loid, Snapshot snapshot, -+ uint32 required, bool abort) -+{ -+ Relation pg_lo_meta; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ sepgsql_sid_t tsid; -+ char auname[NAMEDATALEN]; -+ bool retval; -+ -+ snprintf(auname, sizeof(auname), "blob_%u", loid); -+ -+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, -+ AccessShareLock); -+ -+ ScanKeyInit(&skey, -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loid)); -+ -+ scan = systable_beginscan(pg_lo_meta, -+ LargeObjectMetadataOidIndexId, true, -+ snapshot, 1, &skey); -+ -+ tuple = systable_getnext(scan); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "large object %u does not exist", loid); -+ -+ tsid.relid = LargeObjectMetadataRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_BLOB, -+ required, -+ auname, -+ abort); -+ systable_endscan(scan); -+ -+ heap_close(pg_lo_meta, AccessShareLock); -+ -+ return retval; -+} -+#endif -+ -+Oid -+sepgsql_largeobject_create(Oid loid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ char auname[NAMEDATALEN]; -+ -+ nsid = sepgsql_get_default_blob_secid(MyDatabaseId); -+ snprintf(auname, sizeof(auname), "blob_%u", loid); -+ -+ /* db_blob:{create} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__CREATE, -+ auname, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_largeobject_alter(Oid loid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_largeobject_common(loid, SnapshotNow, -+ SEPG_DB_BLOB__SETATTR, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_largeobject_relabel(Oid loid, char *newLabel) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ char auname[NAMEDATALEN]; -+ -+ nsid.relid = LargeObjectMetadataRelationId; -+ nsid.secid = seclabelTransInput(nsid.relid, newLabel); -+ -+ snprintf(auname, sizeof(auname), "blob_%u", loid); -+ -+ /* db_blob:{setattr relabelfrom} */ -+ sepgsql_largeobject_common(loid, -+ SnapshotNow, -+ SEPG_DB_BLOB__SETATTR | -+ SEPG_DB_BLOB__RELABELFROM, -+ true); -+ /* db_blob:{relabelto} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__RELABELTO, -+ auname, -+ true); -+ return nsid.secid; -+ } -+#endif -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL is not available"))); -+ return InvalidOid; -+} -+ -+void -+sepgsql_largeobject_drop(Oid loid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_largeobject_common(loid, SnapshotNow, -+ SEPG_DB_BLOB__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_largeobject_read(Oid loid, Snapshot snapshot) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_largeobject_common(loid, snapshot, -+ SEPG_DB_BLOB__READ, true); -+ } -+#endif -+} -+ -+void -+sepgsql_largeobject_write(Oid loid, Snapshot snapshot) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_largeobject_common(loid, snapshot, -+ SEPG_DB_BLOB__WRITE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_largeobject_import(Oid loid, const char *filename) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ char auname[NAMEDATALEN]; -+ -+ nsid = sepgsql_get_default_blob_secid(MyDatabaseId); -+ snprintf(auname, sizeof(auname), "blob_%u", loid); -+ -+ /* db_blob:{create} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__CREATE | -+ SEPG_DB_BLOB__WRITE | -+ SEPG_DB_BLOB__IMPORT, -+ auname, -+ true); -+ /* db_file:{read} */ -+ // XXX - todo: add file read checks -+ -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_largeobject_export(Oid loid, Snapshot snapshot, const char *filename) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_blob:{read export} */ -+ sepgsql_largeobject_common(loid, snapshot, -+ SEPG_DB_BLOB__READ | -+ SEPG_DB_BLOB__EXPORT, true); -+ /* file:{write} */ -+ // TODO: add security checks -+ } -+#endif -+} -+ -+void -+sepgsql_largeobject_grant(Oid loid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_largeobject_common(loid, SnapshotNow, -+ SEPG_DB_BLOB__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_largeobject_comment(Oid loid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_largeobject_common(loid, SnapshotNow, -+ SEPG_DB_BLOB__SETATTR, true); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/conversion.c b/src/backend/sepgsql/conversion.c -new file mode 100644 -index 0000000..6a60284 ---- /dev/null -+++ b/src/backend/sepgsql/conversion.c -@@ -0,0 +1,147 @@ -+/* -+ * conversion.c -+ * -+ * SELinux hooks related to conversion -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_conversion.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/syscache.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_conversion_common(Oid convOid, uint32 required, bool abort) -+{ -+ sepgsql_sid_t tsid; -+ HeapTuple tuple; -+ const char *auname; -+ bool retval; -+ -+ tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(convOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for conversion %u", convOid); -+ -+ tsid.relid = ConversionRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ auname = NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+ -+static Oid -+get_conversion_namespace(Oid convOid) -+{ -+ HeapTuple tuple; -+ Oid namespaceId = InvalidOid; -+ -+ tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(convOid)); -+ if (HeapTupleIsValid(tuple)) -+ { -+ namespaceId = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace; -+ -+ ReleaseSysCache(tuple); -+ } -+ return namespaceId; -+} -+#endif -+ -+Oid -+sepgsql_conversion_create(const char *convName, -+ Oid namespaceId, Oid conversionFunc) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ -+ nsid = sepgsql_get_default_tuple_secid(ConversionRelationId); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(namespaceId, -+ SEPG_DB_SCHEMA__ADD_NAME, true); -+ /* db_procedure:{install} */ -+ sepgsql_proc_common(conversionFunc, -+ SEPG_DB_PROCEDURE__INSTALL, true); -+ /* db_tuple:{insert} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ convName, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_conversion_alter(Oid convOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_conversion_alter_rename(Oid convOid, const char *newName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_conversion_namespace(convOid); -+ -+ /* db_schema:{add_name remove_name} */ -+ sepgsql_schema_common(namespaceId, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{update} */ -+ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_conversion_drop(Oid convOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_conversion_namespace(convOid); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{delete} */ -+ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_conversion_comment(Oid convOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/database.c b/src/backend/sepgsql/database.c -new file mode 100644 -index 0000000..bee9d36 ---- /dev/null -+++ b/src/backend/sepgsql/database.c -@@ -0,0 +1,201 @@ -+/* -+ * database.c -+ * -+ * SELinux hooks related to database -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_database.h" -+#include "catalog/pg_seclabel.h" -+#include "commands/dbcommands.h" -+#include "miscadmin.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/syscache.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_database_common(Oid datOid, uint32 required, bool abort) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t tsid; -+ const char *auname; -+ bool retval; -+ -+ tuple = SearchSysCache1(DATABASEOID, -+ ObjectIdGetDatum(datOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for database %u", datOid); -+ -+ tsid.relid = DatabaseRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ auname = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_DATABASE, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+#endif -+ -+Oid -+sepgsql_database_create(const char *datName, Oid templateOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ -+ /* compute a default security context */ -+ nsid = sepgsql_get_default_database_secid(templateOid); -+ -+ /* db_database:{create} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_DATABASE, -+ SEPG_DB_DATABASE__CREATE, -+ datName, true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_database_alter(Oid databaseOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_database_common(databaseOid, -+ SEPG_DB_DATABASE__SETATTR, -+ true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_database_relabel(Oid databaseOid, char *new_label) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ char *auname; -+ -+ nsid.relid = DatabaseRelationId; -+ nsid.secid = seclabelTransInput(nsid.relid, new_label); -+ -+ auname = get_database_name(databaseOid); -+ -+ /* db_database:{setattr relabelfrom} */ -+ sepgsql_database_common(databaseOid, -+ SEPG_DB_DATABASE__SETATTR | -+ SEPG_DB_DATABASE__RELABELFROM, -+ true); -+ -+ /* db_database:{relabelto} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_DATABASE, -+ SEPG_DB_DATABASE__RELABELTO, -+ auname, -+ true); -+ pfree(auname); -+ -+ return nsid.secid; -+ } -+#endif -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL is not available"))); -+ return InvalidOid; -+} -+ -+void -+sepgsql_database_drop(Oid databaseOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_database_common(databaseOid, -+ SEPG_DB_DATABASE__DROP, -+ true); -+ } -+#endif -+} -+ -+void -+sepgsql_database_grant(Oid databaseOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_database_common(databaseOid, -+ SEPG_DB_DATABASE__SETATTR, -+ true); -+ } -+#endif -+} -+ -+void -+sepgsql_database_comment(Oid databaseOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_database_common(databaseOid, -+ SEPG_DB_DATABASE__SETATTR, -+ true); -+ } -+#endif -+} -+ -+void -+sepgsql_database_connect(Oid databaseOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ if (!sepgsql_database_common(databaseOid, -+ SEPG_DB_DATABASE__ACCESS, -+ false)) -+ ereport(FATAL, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("permission denied for database \"%s\"", -+ get_database_name(databaseOid)))); -+ } -+#endif -+} -+ -+void -+sepgsql_database_reindex(Oid databaseOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ // TODO: check db_table:{indexon} for each -+ -+ -+ -+ } -+#endif -+} -+ -+void -+sepgsql_database_getattr(Oid databaseOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_database_common(databaseOid, -+ SEPG_DB_DATABASE__GETATTR, -+ true); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/fdw.c b/src/backend/sepgsql/fdw.c -new file mode 100644 -index 0000000..be4a34b ---- /dev/null -+++ b/src/backend/sepgsql/fdw.c -@@ -0,0 +1,296 @@ -+/* -+ * fdw.c -+ * -+ * SELinux hooks related to foreign data wrapper -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_foreign_data_wrapper.h" -+#include "catalog/pg_foreign_server.h" -+#include "catalog/pg_user_mapping.h" -+#include "miscadmin.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/syscache.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort) -+{ -+ Form_pg_foreign_data_wrapper fdwForm; -+ HeapTuple tuple; -+ sepgsql_sid_t tsid; -+ bool retval; -+ -+ tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for foreign-data-wrapper %u", fdwOid); -+ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple); -+ -+ tsid.relid = ForeignDataWrapperRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ NameStr(fdwForm->fdwname), -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+ -+bool -+sepgsql_fserver_common(Oid fservOid, uint32 required, bool abort) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t tsid; -+ const char *auname; -+ bool retval; -+ -+ tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fservOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for foreign-server %u", fservOid); -+ -+ tsid.relid = ForeignServerRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ auname = NameStr(((Form_pg_foreign_server) GETSTRUCT(tuple))->srvname); -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+ -+static char * -+get_fserver_name(Oid fservOid) -+{ -+ Form_pg_foreign_server servForm; -+ HeapTuple tuple; -+ char *srvname = NULL; -+ -+ tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fservOid)); -+ if (HeapTupleIsValid(tuple)) -+ { -+ servForm = (Form_pg_foreign_server) GETSTRUCT(tuple); -+ -+ srvname = pstrdup(NameStr(servForm->srvname)); -+ -+ ReleaseSysCache(tuple); -+ } -+ return srvname; -+} -+ -+bool -+sepgsql_user_mapping_common(Oid umapOid, uint32 required, bool abort) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t tsid; -+ Oid umuser; -+ Oid umserver; -+ char auname[NAMEDATALEN * 2 + 10]; -+ bool retval; -+ -+ tuple = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umapOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for user mapping %u", umapOid); -+ -+ tsid.relid = UserMappingRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ umuser = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umuser; -+ umserver = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umserver; -+ -+ snprintf(auname, sizeof(auname), "%s@%s", -+ OidIsValid(umuser) ? GetUserNameFromId(umuser) : "public", -+ get_fserver_name(umserver)); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+#endif -+ -+Oid -+sepgsql_fdw_create(const char *fdwName, Oid validatorFunc) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid -+ = sepgsql_get_default_tuple_secid(ForeignDataWrapperRelationId); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(validatorFunc)) -+ sepgsql_proc_common(validatorFunc, -+ SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ /* db_tuple:{insert} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ fdwName, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_fdw_alter(Oid fdwOid, Oid newValidator) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_procedure:{install} */ -+ if (OidIsValid(newValidator)) -+ sepgsql_proc_common(newValidator, -+ SEPG_DB_PROCEDURE__INSTALL, true); -+ /* db_tuple:{update} */ -+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_fdw_drop(Oid fdwOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{delete} */ -+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_fdw_grant(Oid fdwOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_fserver_create(const char *fservName, Oid fdwOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid -+ = sepgsql_get_default_tuple_secid(ForeignServerRelationId); -+ -+ /* db_tuple:{insert} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ fservName, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_fserver_alter(Oid fservOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_fserver_drop(Oid fservOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_fserver_grant(Oid fservOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_user_mapping_create(Oid umuserId, Oid fservOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ char auname[NAMEDATALEN * 2 + 10]; -+ sepgsql_sid_t nsid -+ = sepgsql_get_default_tuple_secid(UserMappingRelationId); -+ -+ snprintf(auname, sizeof(auname), "%s@%s", -+ OidIsValid(umuserId) ? GetUserNameFromId(umuserId) : "public", -+ get_fserver_name(fservOid)); -+ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ auname, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_user_mapping_alter(Oid umapOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_user_mapping_common(umapOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_user_mapping_drop(Oid umapOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{delete} */ -+ sepgsql_user_mapping_common(umapOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/file.c b/src/backend/sepgsql/file.c -new file mode 100644 -index 0000000..9a9e8a3 ---- /dev/null -+++ b/src/backend/sepgsql/file.c -@@ -0,0 +1,287 @@ -+/* -+ * file.c -+ * -+ * SELinux hooks related to filesystem -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+ -+#include -+#ifdef HAVE_SELINUX -+#include -+#endif -+#include -+#include -+#include -+ -+#ifdef HAVE_SELINUX -+static uint16 -+sepgsql_file_class(const char *filename, bool abort) -+{ -+ struct stat stbuf; -+ uint16 tclass; -+ -+ if (stat(filename, &stbuf) < 0) -+ { -+ if (abort) -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not stat file \"%s\": %m", filename))); -+ return 0; -+ } -+ -+ /* decide object class to be applied */ -+ 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; -+ -+ return tclass; -+} -+ -+static bool -+sepgsql_file_common(const char *filename, uint16 tclass, uint32 required, bool abort) -+{ -+ struct stat stbuf; -+ char *scontext = sepgsql_get_client_label(); -+ char *tcontext; -+ char *temp; -+ bool retval; -+ -+ if (stat(filename, &stbuf) < 0) -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not stat file \"%s\"", filename))); -+ -+ if (getfilecon_raw(filename, &temp) < 0) -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not get security context \"%s\"", filename))); -+ -+ PG_TRY(); -+ { -+ tcontext = pstrdup(temp); -+ } -+ PG_CATCH(); -+ { -+ freecon(temp); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(temp); -+ -+ retval = sepgsql_compute_perms(scontext, tcontext, -+ tclass, required, -+ filename, abort); -+ pfree(tcontext); -+ -+ return retval; -+} -+#endif -+ -+void -+sepgsql_file_getattr(const char *filename) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ uint16 tclass = sepgsql_file_class(filename, true); -+ -+ sepgsql_file_common(filename, tclass, -+ SEPG_FILE__GETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_file_read(const char *filename) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ uint16 tclass = sepgsql_file_class(filename, true); -+ -+ sepgsql_file_common(filename, tclass, -+ SEPG_FILE__READ, true); -+ } -+#endif -+} -+ -+void -+sepgsql_file_write(const char *filename, bool may_create) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ uint16 tclass = sepgsql_file_class(filename, !may_create); -+ char *dir_name; -+ char *tcontext; -+ char *ncontext; -+ char *temp; -+ -+ /* write an existing file */ -+ if (tclass != 0) -+ { -+ sepgsql_file_common(filename, tclass, -+ SEPG_FILE__WRITE, true); -+ return; -+ } -+ -+ /* create a new file, and write it */ -+ dir_name = dirname(pstrdup(filename)); -+ -+ if (sepgsql_file_class(dir_name, true) != SEPG_CLASS_DIR) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not a directory", dir_name))); -+ -+ if (getfilecon(dir_name, &temp) < 0) -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not get security context \"%s\"", dir_name))); -+ PG_TRY(); -+ { -+ tcontext = pstrdup(temp); -+ } -+ PG_CATCH(); -+ { -+ freecon(temp); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(temp); -+ -+ /* dir:{add_name} */ -+ sepgsql_file_common(dir_name, SEPG_CLASS_DIR, -+ SEPG_DIR__ADD_NAME, true); -+ -+ /* file:{create write} */ -+ ncontext = sepgsql_compute_create(sepgsql_get_client_label(), -+ tcontext, SEPG_CLASS_FILE); -+ sepgsql_compute_perms(sepgsql_get_client_label(), -+ ncontext, -+ SEPG_CLASS_FILE, -+ SEPG_FILE__CREATE | -+ SEPG_FILE__WRITE, -+ filename, true); -+ } -+#endif -+} -+ -+void -+sepgsql_file_append(const char *filename) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ if (sepgsql_file_class(filename, true) != SEPG_CLASS_FILE) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not a regular file", filename))); -+ -+ sepgsql_file_common(filename, SEPG_CLASS_FILE, -+ SEPG_FILE__APPEND, true); -+ } -+#endif -+} -+ -+void -+sepgsql_file_unlink(const char *filename) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ uint16 tclass = sepgsql_file_class(filename, true); -+ -+ sepgsql_file_common(filename, tclass, SEPG_FILE__UNLINK, true); -+ } -+#endif -+} -+ -+void -+sepgsql_file_rename(const char *oldpath, const char *newpath) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ char *olddir = dirname(pstrdup(oldpath)); -+ char *newdir = dirname(pstrdup(newpath)); -+ uint16 tclass; -+ uint32 required; -+ -+ /* file:{setattr} on oldpath */ -+ tclass = sepgsql_file_class(oldpath, true); -+ sepgsql_file_common(oldpath, tclass, -+ SEPG_FILE__RENAME, true); -+ -+ if (sepgsql_file_class(olddir, true) != SEPG_CLASS_DIR) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not a directory", olddir))); -+ -+ required = SEPG_DIR__SEARCH | SEPG_DIR__REMOVE_NAME; -+ if (strcmp(olddir, newdir) == 0) -+ { -+ /* dir:{search remove_name add_name} */ -+ required |= SEPG_DIR__ADD_NAME; -+ sepgsql_file_common(olddir, SEPG_CLASS_DIR, required, true); -+ } -+ else -+ { -+ /* dir:{search remove_name (reparent)} */ -+ if (tclass == SEPG_CLASS_DIR) -+ required |= SEPG_DIR__REPARENT; -+ sepgsql_file_common(olddir, SEPG_CLASS_DIR, required, true); -+ -+ if (sepgsql_file_class(newdir, true) != SEPG_CLASS_DIR) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not a directory", newdir))); -+ -+ /* dir:{add_name} */ -+ sepgsql_file_common(newdir, SEPG_CLASS_DIR, -+ SEPG_DIR__ADD_NAME, true); -+ } -+ -+ /* file:{unlink} or dir:{DIR__RMDIR}, if necessary */ -+ tclass = sepgsql_file_class(newpath, false); -+ if (tclass == SEPG_CLASS_DIR) -+ sepgsql_file_common(newpath, tclass, SEPG_DIR__RMDIR, true); -+ else if (tclass > 0) -+ sepgsql_file_common(newpath, tclass, SEPG_FILE__UNLINK, true); -+ } -+#endif -+} -+ -+void -+sepgsql_dir_list(const char *dirname) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ uint16 tclass = sepgsql_file_class(dirname, true); -+ -+ if (tclass != SEPG_CLASS_DIR) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not directory", dirname))); -+ -+ sepgsql_file_common(dirname, tclass, SEPG_DIR__READ, true); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/label.c b/src/backend/sepgsql/label.c -new file mode 100644 -index 0000000..ec8244d ---- /dev/null -+++ b/src/backend/sepgsql/label.c -@@ -0,0 +1,588 @@ -+/* -+ * label.c -+ * SE-PostgreSQL security label management -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "access/relscan.h" -+#include "access/xact.h" -+#include "catalog/pg_attribute.h" -+#include "catalog/pg_class.h" -+#include "catalog/pg_database.h" -+#include "catalog/pg_largeobject_metadata.h" -+#include "catalog/pg_namespace.h" -+#include "catalog/pg_proc.h" -+#include "catalog/pg_seclabel.h" -+#include "catalog/pg_type.h" -+#include "commands/dbcommands.h" -+#include "miscadmin.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/fmgroids.h" -+#include "utils/lsyscache.h" -+#include "utils/rel.h" -+#include "utils/syscache.h" -+#include "utils/tqual.h" -+#include // for workaround hack -+#include -+#include -+ -+/* -+ * guc parameter to turn on/off mcstrans -+ */ -+bool sepgsql_mcstrans; -+ -+sepgsql_sid_t -+sepgsql_move_secid(Oid dst_relid, sepgsql_sid_t ssid) -+{ -+ char *label; -+ sepgsql_sid_t dsid = { .relid = dst_relid, -+ .secid = InvalidOid }; -+ -+ label = seclabelRawOutput(ssid.relid, ssid.secid); -+ if (label) -+ { -+ dsid.secid = seclabelRawInput(dsid.relid, label); -+ -+ pfree(label); -+ } -+ return dsid; -+} -+ -+static sepgsql_sid_t -+get_default_secid_with_database(Oid relOid, Oid databaseOid, uint16 tclass) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t datsid; -+ -+ tuple = SearchSysCache1(DATABASEOID, -+ ObjectIdGetDatum(databaseOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for database: %u", databaseOid); -+ -+ datsid.relid = DatabaseRelationId; -+ datsid.secid = HeapTupleGetSecid(tuple); -+ -+ ReleaseSysCache(tuple); -+ -+ return sepgsql_client_create_secid(datsid, tclass, relOid); -+} -+ -+static sepgsql_sid_t -+get_default_secid_with_schema(Oid relOid, Oid namespaceOid, uint16 tclass) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t nspsid; -+ -+ tuple = SearchSysCache1(NAMESPACEOID, -+ ObjectIdGetDatum(namespaceOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for namespace: %u", namespaceOid); -+ -+ nspsid.relid = NamespaceRelationId; -+ nspsid.secid = HeapTupleGetSecid(tuple); -+ -+ ReleaseSysCache(tuple); -+ -+ return sepgsql_client_create_secid(nspsid, tclass, relOid); -+} -+ -+static sepgsql_sid_t -+get_default_secid_with_table(Oid relOid, Oid tableOid, uint16 tclass) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t tblsid; -+ -+ tuple = SearchSysCache1(RELOID, -+ ObjectIdGetDatum(tableOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation: %u", tableOid); -+ -+ tblsid.relid = RelationRelationId; -+ tblsid.secid = HeapTupleGetSecid(tuple); -+ -+ ReleaseSysCache(tuple); -+ -+ return sepgsql_client_create_secid(tblsid, tclass, relOid); -+} -+ -+sepgsql_sid_t -+sepgsql_get_default_database_secid(Oid templateOid) -+{ -+ return get_default_secid_with_database(DatabaseRelationId, -+ templateOid, -+ SEPG_CLASS_DB_DATABASE); -+} -+ -+sepgsql_sid_t -+sepgsql_get_default_schema_secid(Oid databaseOid) -+{ -+ return get_default_secid_with_database(NamespaceRelationId, -+ databaseOid, -+ SEPG_CLASS_DB_SCHEMA); -+} -+ -+sepgsql_sid_t -+sepgsql_get_default_table_secid(Oid namespaceOid) -+{ -+ return get_default_secid_with_schema(RelationRelationId, -+ namespaceOid, -+ SEPG_CLASS_DB_TABLE); -+} -+ -+sepgsql_sid_t -+sepgsql_get_default_sequence_secid(Oid namespaceOid) -+{ -+ return get_default_secid_with_schema(RelationRelationId, -+ namespaceOid, -+ SEPG_CLASS_DB_SEQUENCE); -+} -+ -+sepgsql_sid_t -+sepgsql_get_default_view_secid(Oid namespaceOid) -+{ -+ return get_default_secid_with_schema(RelationRelationId, -+ namespaceOid, -+ SEPG_CLASS_DB_VIEW); -+} -+ -+sepgsql_sid_t -+sepgsql_get_default_proc_secid(Oid namespaceOid) -+{ -+ return get_default_secid_with_schema(ProcedureRelationId, -+ namespaceOid, -+ SEPG_CLASS_DB_PROCEDURE); -+} -+ -+sepgsql_sid_t -+sepgsql_get_default_column_secid(Oid tableOid) -+{ -+ return get_default_secid_with_table(AttributeRelationId, -+ tableOid, -+ SEPG_CLASS_DB_COLUMN); -+} -+ -+sepgsql_sid_t -+sepgsql_get_default_tuple_secid(Oid tableOid) -+{ -+ return get_default_secid_with_table(tableOid, -+ tableOid, -+ SEPG_CLASS_DB_TUPLE); -+} -+ -+sepgsql_sid_t -+sepgsql_get_default_blob_secid(Oid databaseOid) -+{ -+ return get_default_secid_with_database(LargeObjectMetadataRelationId, -+ databaseOid, -+ SEPG_CLASS_DB_BLOB); -+} -+ -+Oid -+sepgsql_get_default_secid(Relation rel, HeapTuple tuple) -+{ -+ Oid namespaceId; -+ sepgsql_sid_t nsid -+ = { .relid = RelationGetRelid(rel), .secid = InvalidOid }; -+ -+ switch (RelationGetRelid(rel)) -+ { -+ case DatabaseRelationId: -+ case RelationRelationId: -+ case AttributeRelationId: -+ elog(WARNING, "Bug? a new tuple without security id on \"%s\"", -+ RelationGetRelationName(rel)); -+ break; -+ -+ case NamespaceRelationId: -+ nsid = sepgsql_get_default_schema_secid(MyDatabaseId); -+ break; -+ -+ case ProcedureRelationId: -+ namespaceId = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; -+ nsid = sepgsql_get_default_proc_secid(namespaceId); -+ break; -+ -+ case LargeObjectMetadataRelationId: -+ nsid = sepgsql_get_default_blob_secid(MyDatabaseId); -+ break; -+ -+ default: -+ nsid = sepgsql_get_default_tuple_secid(RelationGetRelid(rel)); -+ break; -+ } -+ return nsid.secid; -+} -+ -+static Oid -+sepgsql_initial_labeling_lookup(struct selabel_handle *sehnd, -+ Oid relationId, HeapTuple tuple) -+{ -+ Form_pg_database datForm; -+ Form_pg_namespace nspForm; -+ Form_pg_class clsForm; -+ Form_pg_attribute attForm; -+ Form_pg_proc proForm; -+ char namebuf[NAMEDATALEN * 3 + 10]; -+ Oid namespaceId; -+ Oid securityId = InvalidOid; -+ int type = 0; -+ security_context_t context; -+ -+ switch (relationId) -+ { -+ case DatabaseRelationId: -+ datForm = (Form_pg_database) GETSTRUCT(tuple); -+ snprintf(namebuf, sizeof(namebuf), "%s", -+ NameStr(datForm->datname)); -+ type = SELABEL_DB_DATABASE; -+ break; -+ -+ case NamespaceRelationId: -+ nspForm = (Form_pg_namespace) GETSTRUCT(tuple); -+ snprintf(namebuf, sizeof(namebuf), "%s.%s", -+ get_database_name(MyDatabaseId), -+ NameStr(nspForm->nspname)); -+ type = SELABEL_DB_SCHEMA; -+ break; -+ -+ case RelationRelationId: -+ clsForm = (Form_pg_class) GETSTRUCT(tuple); -+ namespaceId = clsForm->relnamespace; -+ switch (clsForm->relkind) -+ { -+ case RELKIND_RELATION: -+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", -+ get_database_name(MyDatabaseId), -+ get_namespace_name(namespaceId), -+ NameStr(clsForm->relname)); -+ type = SELABEL_DB_TABLE; -+ break; -+ -+ case RELKIND_SEQUENCE: -+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", -+ get_database_name(MyDatabaseId), -+ get_namespace_name(namespaceId), -+ NameStr(clsForm->relname)); -+ type = SELABEL_DB_SEQUENCE; -+ break; -+ -+ case RELKIND_VIEW: -+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", -+ get_database_name(MyDatabaseId), -+ get_namespace_name(namespaceId), -+ NameStr(clsForm->relname)); -+ type = SELABEL_DB_VIEW; -+ break; -+ -+ case RELKIND_INDEX: { -+ HeapTuple tbltup; -+ HeapTuple indtup; -+ Oid tableId; -+ -+ indtup = SearchSysCache1(INDEXRELID, -+ ObjectIdGetDatum(HeapTupleGetOid(tuple))); -+ if (!HeapTupleIsValid(indtup)) -+ elog(ERROR, "cache lookup failed for index %u", -+ HeapTupleGetOid(tuple)); -+ -+ tableId = ((Form_pg_index) GETSTRUCT(indtup))->indrelid; -+ tbltup = SearchSysCache1(RELOID, ObjectIdGetDatum(tableId)); -+ if (!HeapTupleIsValid(tbltup)) -+ elog(ERROR, "cache lookup failed for relation %u", tableId); -+ -+ securityId = sepgsql_initial_labeling_lookup(sehnd, RelationRelationId, tbltup); -+ -+ ReleaseSysCache(tbltup); -+ ReleaseSysCache(indtup); -+ -+ return securityId; -+ } -+ case RELKIND_TOASTVALUE: { -+ HeapTuple tbltup; -+ Oid tableId; -+ -+ /* -+ * XXX - we assume all the toast relation's name is -+ * "pg_toast_%u", and the "%u" shall be replaced by OID -+ * of the relation which owns the toast relation -+ */ -+ tableId = strtoul(NameStr(clsForm->relname) + 9, NULL, 10); -+ -+ tbltup = SearchSysCache1(RELOID, -+ ObjectIdGetDatum(tableId)); -+ if (!HeapTupleIsValid(tbltup)) -+ elog(ERROR, "cache lookup failed for relation %u", tableId); -+ -+ securityId = sepgsql_initial_labeling_lookup(sehnd, RelationRelationId, tbltup); -+ -+ ReleaseSysCache(tbltup); -+ -+ return securityId; -+ } -+ case RELKIND_COMPOSITE_TYPE: { -+ HeapTuple typtup; -+ -+ typtup = SearchSysCache1(TYPEOID, -+ ObjectIdGetDatum(clsForm->reltype)); -+ if (!HeapTupleIsValid(typtup)) -+ elog(ERROR, "cache lookup failed for type %u", clsForm->reltype); -+ -+ securityId = sepgsql_initial_labeling_lookup(sehnd, TypeRelationId, typtup); -+ -+ ReleaseSysCache(typtup); -+ -+ return securityId; -+ } -+ default: -+ elog(ERROR, "unexpected relkind %c of \"%s\"", -+ clsForm->relkind, NameStr(clsForm->relname)); -+ break; -+ } -+ break; -+ -+ case AttributeRelationId: -+ attForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ if (get_rel_relkind(attForm->attrelid) == RELKIND_RELATION) -+ { -+ namespaceId = get_rel_namespace(attForm->attrelid); -+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s.%s", -+ get_database_name(MyDatabaseId), -+ get_namespace_name(namespaceId), -+ get_rel_name(attForm->attrelid), -+ NameStr(attForm->attname)); -+ type = SELABEL_DB_COLUMN; -+ } -+ else -+ { -+ HeapTuple classTup; -+ -+ classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(attForm->attrelid)); -+ if (!HeapTupleIsValid(classTup)) -+ elog(ERROR, "cache lookup failed for relation %u", attForm->attrelid); -+ -+ securityId = sepgsql_initial_labeling_lookup(sehnd, RelationRelationId, classTup); -+ -+ ReleaseSysCache(classTup); -+ -+ return securityId; -+ } -+ break; -+ -+ case ProcedureRelationId: -+ proForm = (Form_pg_proc) GETSTRUCT(tuple); -+ namespaceId = proForm->pronamespace; -+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", -+ get_database_name(MyDatabaseId), -+ get_namespace_name(namespaceId), -+ NameStr(proForm->proname)); -+ type = SELABEL_DB_PROCEDURE; -+ break; -+ -+ case LargeObjectMetadataRelationId: -+ snprintf(namebuf, sizeof(namebuf), "%s.%u", -+ get_database_name(MyDatabaseId), -+ HeapTupleGetOid(tuple)); -+ type = SELABEL_DB_BLOB; -+ break; -+ -+ default: -+ namespaceId = get_rel_namespace(relationId); -+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", -+ get_database_name(MyDatabaseId), -+ get_namespace_name(namespaceId), -+ get_rel_name(relationId)); -+ type = SELABEL_DB_TUPLE; -+ break; -+ } -+ -+ if (selabel_lookup_raw(sehnd, &context, namebuf, type) == 0) -+ { -+ PG_TRY(); -+ { -+ securityId = seclabelTransInput(relationId, context); -+ } -+ PG_CATCH(); -+ { -+ freecon(context); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(context); -+ } -+ else if (errno == ENOENT) -+ elog(WARNING, -+ "SELinux: no initial label assigned for %s (type=%d), skipping", -+ namebuf, type); -+ else -+ elog(ERROR, -+ "SELinux: could not determine initial security label for %s (type=%d): %m", -+ namebuf, type); -+ -+ return securityId; -+} -+ -+void -+sepgsql_initial_labeling(void) -+{ -+ Relation classRel; -+ SysScanDesc classScan; -+ ScanKeyData classSkey; -+ HeapTuple classTup; -+ struct selabel_handle *sehnd; -+ -+ Assert(IsBootstrapProcessingMode()); -+ -+ StartTransactionCommand(); -+ -+ sehnd = selabel_open(SELABEL_CTX_DB, NULL, 0); -+ if (!sehnd) -+ elog(ERROR, "failed to open initial selinux label"); -+ -+ classRel = heap_open(RelationRelationId, AccessShareLock); -+ -+ ScanKeyInit(&classSkey, -+ Anum_pg_class_relhassecids, -+ BTEqualStrategyNumber, F_BOOLEQ, -+ BoolGetDatum(true)); -+ -+ classScan = systable_beginscan(classRel, InvalidOid, false, -+ SnapshotNow, 1, &classSkey); -+ -+ while (HeapTupleIsValid(classTup = systable_getnext(classScan))) -+ { -+ Relation rel; -+ HeapScanDesc scan; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ Oid relOid = HeapTupleGetOid(classTup); -+ -+ Assert(((Form_pg_class) GETSTRUCT(classTup))->relhassecids); -+ -+ rel = heap_open(relOid, RowExclusiveLock); -+ -+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); -+ -+ while (HeapTupleIsValid(oldtup = heap_getnext(scan, ForwardScanDirection))) -+ { -+ Oid securityId = sepgsql_initial_labeling_lookup(sehnd, relOid, oldtup); -+ -+ /* -+ * inplace-updating -+ */ -+ newtup = heap_copytuple(oldtup); -+ -+ HeapTupleSetSecid(newtup, securityId); -+ -+ heap_inplace_update(rel, newtup); -+ -+ heap_freetuple(newtup); -+ } -+ -+ heap_endscan(scan); -+ -+ heap_close(rel, RowExclusiveLock); -+ } -+ systable_endscan(classScan); -+ -+ heap_close(classRel, AccessShareLock); -+ -+ selabel_close(sehnd); -+ -+ CommitTransactionCommand(); -+} -+ -+char * -+sepgsql_mcstrans_in(char *trans_label) -+{ -+ security_context_t raw_label; -+ security_context_t result; -+ -+ if (!sepgsql_mcstrans) -+ return trans_label; -+ -+ if (selinux_trans_to_raw_context(trans_label, &raw_label) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("SELinux: unable to translate \"%s\"", trans_label))); -+ PG_TRY(); -+ { -+ result = pstrdup(raw_label); -+ } -+ PG_CATCH(); -+ { -+ freecon(raw_label); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(raw_label); -+ -+ return result; -+} -+ -+char * -+sepgsql_mcstrans_out(char *raw_label) -+{ -+ security_context_t trans_label; -+ security_context_t result; -+ -+ if (!sepgsql_mcstrans) -+ return raw_label; -+ -+ if (selinux_raw_to_trans_context(raw_label, &trans_label) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("SELinux: unable to translate \"%s\"", raw_label))); -+ PG_TRY(); -+ { -+ result = pstrdup(trans_label); -+ } -+ PG_CATCH(); -+ { -+ freecon(trans_label); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(trans_label); -+ -+ return result; -+} -+ -+char * -+sepgsql_rawlabel_in(char *label) -+{ -+ if (!label || security_check_context_raw(label) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INVALID_NAME), -+ errmsg("invalid security context \"%s\"", label))); -+ return label; -+} -+ -+char * -+sepgsql_rawlabel_out(char *label) -+{ -+ if (!label || security_check_context_raw(label) < 0) -+ { -+ security_context_t unlabeled_label; -+ -+ if (security_get_initial_context_raw("unlabeled", -+ &unlabeled_label) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("unable to get unlabeled security context"))); -+ PG_TRY(); -+ { -+ label = pstrdup(unlabeled_label); -+ } -+ PG_CATCH(); -+ { -+ freecon(unlabeled_label); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(unlabeled_label); -+ } -+ return label; -+} -diff --git a/src/backend/sepgsql/misc.c b/src/backend/sepgsql/misc.c -new file mode 100644 -index 0000000..377a72b ---- /dev/null -+++ b/src/backend/sepgsql/misc.c -@@ -0,0 +1,147 @@ -+/* -+ * misc.c -+ * -+ * SELinux hooks related to misc features -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "libpq/libpq-be.h" -+#include "miscadmin.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/builtins.h" -+#include "utils/fmgroids.h" -+#include "utils/guc.h" -+ -+#include -+ -+/* -+ * sepgsql_client_label -+ * -+ * security context of the peer process -+ */ -+static char *sepgsql_client_label = NULL; -+ -+char * -+sepgsql_get_client_label(void) -+{ -+ return sepgsql_client_label; -+} -+ -+char * -+sepgsql_set_client_label(char *new_label) -+{ -+ char *old_label = sepgsql_client_label; -+ -+ sepgsql_client_label = new_label; -+ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* -+ * NOTE: When an error happened prior to initialization of -+ * SE-PostgreSQL, NULL maight be delivered. In this case, -+ * we don't need to reset avc. -+ */ -+ if (new_label != NULL) -+ sepgsql_avc_switch(new_label); -+ } -+#endif -+ return old_label; -+} -+ -+void -+sepgsql_post_bootstraping(void) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ sepgsql_initial_labeling(); -+#endif -+} -+ -+void -+sepgsql_initialize(void) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ security_context_t context; -+ -+ /* init userspace avc */ -+ sepgsql_avc_init(); -+ -+ /* init privilege of the client */ -+ if (!MyProcPort) -+ { -+ /* -+ * SE-PgSQL does not prevent anything in single-user mode. -+ */ -+ sepostgresql_mode = SEPGSQL_MODE_INTERNAL; -+ -+ if (getprevcon_raw(&context) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("SELinux: could not get server context"))); -+ } -+ else -+ { -+ if (getpeercon_raw(MyProcPort->sock, &context) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("SELinux: could not get client context"))); -+ } -+ sepgsql_set_client_label(context); -+ -+ return; -+ } -+#endif -+ if (default_with_secids) -+ { -+ default_with_secids = false; -+ elog(LOG, "guc: default_with_secid was turned off " -+ "because no label based access control is availabel now"); -+ } -+} -+ -+bool -+sepgsql_worker_needed(void) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ return true; -+#endif -+ return false; -+} -+ -+void -+sepgsql_worker_main(void) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_avc_worker_main(); -+ return; -+ } -+#endif -+ elog(FATAL, "Bug? try to launch worker process without security provider"); -+} -+ -+Datum -+sepgsql_getcon(PG_FUNCTION_ARGS) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ char *label = sepgsql_get_client_label(); -+ -+ PG_RETURN_TEXT_P(cstring_to_text(label)); -+ } -+#endif -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL is not available"))); -+ PG_RETURN_NULL(); -+} -diff --git a/src/backend/sepgsql/operator.c b/src/backend/sepgsql/operator.c -new file mode 100644 -index 0000000..7960525 ---- /dev/null -+++ b/src/backend/sepgsql/operator.c -@@ -0,0 +1,454 @@ -+/* -+ * operator.c -+ * -+ * SELinux hooks related to operators -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_operator.h" -+#include "catalog/pg_opclass.h" -+#include "catalog/pg_opfamily.h" -+#include "catalog/pg_seclabel.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/builtins.h" -+#include "utils/lsyscache.h" -+#include "utils/syscache.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_operator_common(Oid operOid, uint32 required, bool abort) -+{ -+ sepgsql_sid_t tsid; -+ char *auname; -+ bool retval; -+ -+ tsid.relid = OperatorRelationId; -+ tsid.secid = GetSysCacheSecid1(OPEROID, ObjectIdGetDatum(operOid)); -+ -+ auname = format_operator(operOid); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ pfree(auname); -+ -+ return retval; -+} -+ -+bool -+sepgsql_opclass_common(Oid opcOid, uint32 required, bool abort) -+{ -+ sepgsql_sid_t tsid; -+ HeapTuple tuple; -+ const char *auname; -+ bool retval; -+ -+ tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for operator class %u", opcOid); -+ -+ tsid.relid = OperatorClassRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ auname = NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+ -+bool -+sepgsql_opfamily_common(Oid opfOid, uint32 required, bool abort) -+{ -+ sepgsql_sid_t tsid; -+ HeapTuple tuple; -+ const char *auname; -+ bool retval; -+ -+ tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for operator family %u", opfOid); -+ -+ tsid.relid = OperatorFamilyRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ auname = NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+ -+static Oid -+get_operator_namespace(Oid operOid) -+{ -+ HeapTuple tuple; -+ Oid namespaceId; -+ -+ tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid)); -+ if (HeapTupleIsValid(tuple)) -+ { -+ namespaceId = ((Form_pg_operator) GETSTRUCT(tuple))->oprnamespace; -+ -+ ReleaseSysCache(tuple); -+ } -+ return namespaceId; -+} -+ -+static Oid -+get_opclass_namespace(Oid opcOid) -+{ -+ HeapTuple tuple; -+ Oid namespaceId = InvalidOid; -+ -+ tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcOid)); -+ if (HeapTupleIsValid(tuple)) -+ { -+ namespaceId = ((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace; -+ -+ ReleaseSysCache(tuple); -+ } -+ return namespaceId; -+} -+ -+static Oid -+get_opfamily_namespace(Oid opfOid) -+{ -+ HeapTuple tuple; -+ Oid namespaceId = InvalidOid; -+ -+ tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfOid)); -+ if (HeapTupleIsValid(tuple)) -+ { -+ namespaceId = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace; -+ -+ ReleaseSysCache(tuple); -+ } -+ return namespaceId; -+} -+ -+#endif -+ -+Oid -+sepgsql_operator_create(const char *operName, Oid replaced, Oid namespaceId, -+ Oid codeFunc, Oid restrictFunc, Oid joinFunc, -+ Oid commutatorOp, Oid negatorOp) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ uint32 required; -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_tuple:{insert} */ -+ if (OidIsValid(replaced)) -+ { -+ nsid.relid = OperatorRelationId; -+ nsid.secid = GetSysCacheSecid1(OPEROID, -+ ObjectIdGetDatum(replaced)); -+ required = SEPG_DB_TUPLE__UPDATE; -+ } -+ else -+ { -+ nsid = sepgsql_get_default_tuple_secid(OperatorRelationId); -+ required = SEPG_DB_TUPLE__INSERT; -+ } -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ operName, -+ true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(codeFunc)) -+ sepgsql_proc_common(codeFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(restrictFunc)) -+ sepgsql_proc_common(restrictFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(joinFunc)) -+ sepgsql_proc_common(joinFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ /* -+ * XXX - we should check anything on he commutatorOp/negatorOp -+ */ -+ -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_operator_alter(Oid operOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_operator_common(operOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_operator_relabel(Oid operOid, char *newLabel) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ -+ nsid.relid = OperatorRelationId; -+ nsid.secid = seclabelTransInput(OperatorRelationId, newLabel); -+ -+ /* db_tuple:{update relabelfrom} */ -+ sepgsql_operator_common(operOid, -+ SEPG_DB_TUPLE__UPDATE | -+ SEPG_DB_TUPLE__RELABELFROM, -+ true); -+ -+ /* db_tuple:{relabelto} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__RELABELTO, -+ format_operator(operOid), -+ true); -+ return nsid.secid; -+ } -+#endif -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL is not available"))); -+ return InvalidOid; -+} -+ -+void -+sepgsql_operator_drop(Oid operOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_operator_namespace(operOid); -+ -+ /* db_namespace:{remove_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{delete} */ -+ sepgsql_operator_common(operOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_operator_comment(Oid operOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_operator_common(operOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_opclass_create(const char *opcName, Oid namespaceId, -+ Oid typeOid, Oid opfamilyOid, Oid storageOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_TUPLE__UPDATE, true); -+ -+ /* db_tuple:{insert} */ -+ nsid = sepgsql_get_default_tuple_secid(OperatorRelationId); -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ opcName, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_opclass_alter(Oid opcOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_opclass_alter_rename(Oid opcOid, const char *newName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_opclass_namespace(opcOid); -+ -+ /* db_schema:{add_name remove_name} */ -+ sepgsql_schema_common(namespaceId, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{update} */ -+ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_opclass_drop(Oid opcOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_opclass_namespace(opcOid); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{drop} */ -+ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_opclass_comment(Oid opcOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_opfamily_create(const char *opfName, Oid namespaceId, Oid amOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_tuple:{insert} */ -+ nsid = sepgsql_get_default_tuple_secid(OperatorFamilyRelationId); -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ opfName, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_opfamily_alter(Oid opfOid, bool isDrop, Oid amOid, -+ List *operators, List *procedures) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); -+ -+ /* XXX - to do we should install checks? */ -+ } -+#endif -+} -+ -+void -+sepgsql_opfamily_alter_rename(Oid opfOid, const char *newName) -+{ -+ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_opfamily_namespace(opfOid); -+ -+ /* db_schema:{add_name remove_name} */ -+ sepgsql_schema_common(namespaceId, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{update} */ -+ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_opfamily_alter_owner(Oid opfOid, Oid newOwner) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_opfamily_drop(Oid opfOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_opfamily_namespace(opfOid); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{delete} */ -+ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_opfamily_comment(Oid opfOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/proc.c b/src/backend/sepgsql/proc.c -new file mode 100644 -index 0000000..1149571 ---- /dev/null -+++ b/src/backend/sepgsql/proc.c -@@ -0,0 +1,366 @@ -+/* -+ * proc.c -+ * -+ * SELinux hooks related to procedures -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_aggregate.h" -+#include "catalog/pg_language.h" -+#include "catalog/pg_namespace.h" -+#include "catalog/pg_proc.h" -+#include "catalog/pg_seclabel.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/builtins.h" -+#include "utils/syscache.h" -+#include "utils/lsyscache.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_proc_common(Oid procOid, uint32 required, bool abort) -+{ -+ sepgsql_sid_t tsid; -+ char *auname; -+ bool retval; -+ -+ tsid.relid = ProcedureRelationId; -+ tsid.secid = GetSysCacheSecid1(PROCOID, -+ ObjectIdGetDatum(procOid)); -+ -+ auname = format_procedure(procOid); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_PROCEDURE, -+ required, -+ auname, -+ abort); -+ pfree(auname); -+ -+ return retval; -+} -+#endif -+ -+Oid -+sepgsql_proc_create(const char *proName, Oid replaced, -+ Oid namespaceId, Oid languageId) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ HeapTuple tuple; -+ sepgsql_sid_t nsid; -+ char *scontext; -+ char *tcontext; -+ uint32 required; -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{create or setattr} */ -+ if (OidIsValid(replaced)) -+ { -+ nsid.relid = ProcedureRelationId; -+ nsid.secid = GetSysCacheSecid1(PROCOID, -+ ObjectIdGetDatum(replaced)); -+ required = SEPG_DB_PROCEDURE__SETATTR; -+ } -+ else -+ { -+ nsid = sepgsql_get_default_proc_secid(namespaceId); -+ required = SEPG_DB_PROCEDURE__CREATE; -+ } -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_PROCEDURE, -+ required, -+ proName, -+ true); -+ -+ /* db_language:{implemente} */ -+ tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(languageId)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for language %u", languageId); -+ -+ scontext = seclabelRawOutput(LanguageRelationId, -+ HeapTupleGetSecid(tuple)); -+ tcontext = seclabelRawOutput(nsid.relid, nsid.secid); -+ -+ sepgsql_compute_perms(scontext, tcontext, -+ SEPG_CLASS_DB_LANGUAGE, -+ SEPG_DB_LANGUAGE__IMPLEMENTE, -+ proName, true); -+ -+ ReleaseSysCache(tuple); -+ -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_proc_alter(Oid procOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_proc_alter_rename(Oid procOid, const char *newName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_func_namespace(procOid); -+ -+ /* db_schema:{remove_name add_name} */ -+ sepgsql_schema_common(namespaceId, -+ SEPG_DB_SCHEMA__REMOVE_NAME | -+ SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{setattr} */ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_proc_alter_schema(Oid procOid, Oid newSchema) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_func_namespace(procOid); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(newSchema, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{setattr} */ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_proc_relabel(Oid procOid, char *new_label) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t tsid; -+ -+ tsid.relid = ProcedureRelationId; -+ tsid.secid = seclabelTransInput(tsid.relid, new_label); -+ -+ /* db_procedure:{setattr relabelfrom} */ -+ sepgsql_proc_common(procOid, -+ SEPG_DB_PROCEDURE__SETATTR | -+ SEPG_DB_PROCEDURE__RELABELFROM, true); -+ -+ /* db_procedure:{relabelto} */ -+ sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_PROCEDURE, -+ SEPG_DB_PROCEDURE__RELABELTO, -+ format_procedure(procOid), -+ true); -+ return tsid.secid; -+ } -+#endif -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL is not available"))); -+ return InvalidOid; -+} -+ -+void -+sepgsql_proc_drop(Oid procOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_func_namespace(procOid); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_procedure:{proc} */ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__DROP, true); -+ } -+#endif -+} -+ -+void -+sepgsql_proc_grant(Oid procOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_proc_comment(Oid procOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_proc_execute(Oid procOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__EXECUTE, true); -+ } -+#endif -+} -+ -+bool -+sepgsql_proc_be_inlined(HeapTuple protup) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t tsid; -+ char *old_label; -+ char *new_label; -+ const char *auname -+ = NameStr(((Form_pg_proc) GETSTRUCT(protup))->proname); -+ -+ tsid.relid = ProcedureRelationId; -+ tsid.secid = HeapTupleGetSecid(protup); -+ -+ if (!sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_PROCEDURE, -+ SEPG_DB_PROCEDURE__EXECUTE, -+ auname, false)) -+ return false; -+ -+ old_label = sepgsql_get_client_label(); -+ new_label = sepgsql_client_create_label(tsid, SEPG_CLASS_PROCESS); -+ if (strcmp(old_label, new_label) != 0) -+ return false; -+ -+ return true; -+ } -+#endif -+ return true; -+} -+ -+char * -+sepgsql_proc_domtrans(HeapTuple protup, MemoryContext mcxt) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t tsid; -+ char *old_label = sepgsql_get_client_label(); -+ char *new_label; -+ char *auname -+ = NameStr(((Form_pg_proc) GETSTRUCT(protup))->proname); -+ -+ tsid.relid = ProcedureRelationId; -+ tsid.secid = HeapTupleGetSecid(protup); -+ -+ new_label = sepgsql_client_create_label(tsid, SEPG_CLASS_PROCESS); -+ -+ if (strcmp(old_label, new_label) == 0) -+ return NULL; -+ -+ /* db_procedure:{entrypoint} */ -+ sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_PROCEDURE, -+ SEPG_DB_PROCEDURE__ENTRYPOINT, -+ auname, -+ true); -+ -+ /* db_process:{transition} */ -+ sepgsql_compute_perms(old_label, -+ new_label, -+ SEPG_CLASS_PROCESS, -+ SEPG_PROCESS__TRANSITION, -+ NULL, -+ true); -+ -+ return MemoryContextStrdup(mcxt, new_label); -+ } -+#endif -+ return NULL; -+} -+ -+Oid -+sepgsql_aggregate_create(const char *aggName, Oid namespaceId, -+ Oid transFunc, Oid finalFunc) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid securityId; -+ -+ /* check normal creation permission */ -+ securityId = sepgsql_proc_create(aggName, InvalidOid, -+ namespaceId, INTERNALlanguageId); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(transFunc)) -+ sepgsql_proc_common(transFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(finalFunc)) -+ sepgsql_proc_common(finalFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return securityId; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_aggregate_execute(Oid aggOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Form_pg_aggregate aggForm; -+ HeapTuple tuple; -+ -+ tuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for aggregate %u", aggOid); -+ -+ aggForm = (Form_pg_aggregate) GETSTRUCT(tuple); -+ -+ /* pg_proc:{execute} */ -+ sepgsql_proc_common(aggOid, SEPG_DB_PROCEDURE__EXECUTE, true); -+ -+ /* pg_proc:{execute} */ -+ if (OidIsValid(aggForm->aggtransfn)) -+ sepgsql_proc_common(aggForm->aggtransfn, -+ SEPG_DB_PROCEDURE__EXECUTE, true); -+ -+ /* pg_proc:{execute} */ -+ if (OidIsValid(aggForm->aggfinalfn)) -+ sepgsql_proc_common(aggForm->aggfinalfn, -+ SEPG_DB_PROCEDURE__EXECUTE, true); -+ -+ ReleaseSysCache(tuple); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/relation.c b/src/backend/sepgsql/relation.c -new file mode 100644 -index 0000000..2604749 ---- /dev/null -+++ b/src/backend/sepgsql/relation.c -@@ -0,0 +1,819 @@ -+/* -+ * relation.c -+ * -+ * SELinux hooks related to relation -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "access/sysattr.h" -+#include "catalog/catalog.h" -+#include "catalog/heap.h" -+#include "catalog/pg_attribute.h" -+#include "catalog/pg_class.h" -+#include "catalog/pg_seclabel.h" -+#include "catalog/pg_type.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/lsyscache.h" -+#include "utils/syscache.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_relation_common(Oid relOid, uint32 required, bool abort) -+{ -+ Form_pg_class classForm; -+ HeapTuple tuple; -+ sepgsql_sid_t tsid; -+ uint16 tclass; -+ bool retval; -+ -+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation %u", relOid); -+ classForm = (Form_pg_class) GETSTRUCT(tuple); -+ -+ tsid.relid = RelationRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ switch (classForm->relkind) -+ { -+ case RELKIND_SEQUENCE: -+ tclass = SEPG_CLASS_DB_SEQUENCE; -+ break; -+ -+ case RELKIND_VIEW: -+ tclass = SEPG_CLASS_DB_VIEW; -+ break; -+ -+ case RELKIND_COMPOSITE_TYPE: -+ tclass = SEPG_CLASS_DB_TUPLE; -+ break; -+ -+ default: -+ tclass = SEPG_CLASS_DB_TABLE; -+ break; -+ } -+ -+ retval = sepgsql_client_perms(tsid, -+ tclass, -+ required, -+ NameStr(classForm->relname), -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+ -+static Bitmapset * -+fixup_whole_row_reference(Oid relOid, int natts, Bitmapset *columns) -+{ -+ Bitmapset *result; -+ AttrNumber attno; -+ -+ attno = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber; -+ if (!bms_is_member(attno, columns)) -+ return columns; /* no need to fixup */ -+ -+ result = bms_copy(columns); -+ result = bms_del_member(result, attno); -+ -+ for (attno=1; attno <= natts; attno++) -+ { -+ Form_pg_attribute attForm; -+ HeapTuple atttup; -+ -+ atttup = SearchSysCache2(ATTNUM, -+ ObjectIdGetDatum(relOid), -+ Int16GetDatum(attno)); -+ if (!HeapTupleIsValid(atttup)) -+ continue; -+ -+ attForm = (Form_pg_attribute) GETSTRUCT(atttup); -+ if (!attForm->attisdropped) -+ { -+ int cindex = attno - FirstLowInvalidHeapAttributeNumber; -+ result = bms_add_member(result, cindex); -+ } -+ ReleaseSysCache(atttup); -+ } -+ -+ return result; -+} -+#endif -+ -+bool -+sepgsql_relation_perms(Oid relOid, AclMode aclmask, -+ Bitmapset *selectedCols, -+ Bitmapset *modifiedCols, bool abort) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Form_pg_class relForm; -+ HeapTuple tuple; -+ sepgsql_sid_t rsid; -+ Bitmapset *selColsEx; -+ Bitmapset *modColsEx; -+ Bitmapset *columns; -+ AttrNumber nattrs; -+ AttrNumber attno; -+ const char *auname; -+ char relkind; -+ uint16 tclass = 0; -+ uint32 required = 0; -+ bool rc = true; -+ -+ /* -+ * Hardwired policy: -+ * SE-PostgreSQL enforces clients cannot modify system catalogs -+ * and access toast values using DML statements in enforcing mode. -+ * Note that it performs in permissive mode during initdb phase. -+ */ -+ if (sepgsql_get_enforce()) -+ { -+ if (IsSystemNamespace(get_rel_namespace(relOid)) && -+ (aclmask & (ACL_UPDATE | ACL_INSERT | ACL_DELETE)) != 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("not allowed to modify system catalog \"%s\"", -+ get_rel_name(relOid)))); -+ -+ if (get_rel_relkind(relOid) == RELKIND_TOASTVALUE) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("not allowed to access toast values \"%s\"", -+ get_rel_name(relOid)))); -+ } -+ -+ /* -+ * check relation's permissions -+ */ -+ tuple = SearchSysCache1(RELOID, -+ ObjectIdGetDatum(relOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation %u", relOid); -+ -+ rsid.relid = RelationRelationId; -+ rsid.secid = HeapTupleGetSecid(tuple); -+ -+ relForm = (Form_pg_class) GETSTRUCT(tuple); -+ auname = NameStr(relForm->relname); -+ relkind = relForm->relkind; -+ nattrs = relForm->relnatts; -+ -+ switch (relkind) -+ { -+ case RELKIND_RELATION: -+ tclass = SEPG_CLASS_DB_TABLE; -+ -+ if (aclmask & ACL_SELECT) -+ required |= SEPG_DB_TABLE__SELECT; -+ if (aclmask & ACL_INSERT) -+ required |= SEPG_DB_TABLE__INSERT; -+ if (aclmask & ACL_UPDATE) -+ required |= (!modifiedCols -+ ? SEPG_DB_TABLE__LOCK -+ : SEPG_DB_TABLE__UPDATE); -+ if (aclmask & ACL_DELETE) -+ required |= SEPG_DB_TABLE__DELETE; -+ break; -+ -+ case RELKIND_SEQUENCE: -+ tclass = SEPG_CLASS_DB_SEQUENCE; -+ if (aclmask & ACL_SELECT) -+ required |= SEPG_DB_SEQUENCE__GET_VALUE; -+ break; -+ -+ case RELKIND_VIEW: -+ tclass = SEPG_CLASS_DB_VIEW; -+ if (aclmask != 0) -+ required |= SEPG_DB_VIEW__EXPAND; -+ break; -+ -+ default: -+ elog(ERROR, "Bug? unexpected relkind %c", relkind); -+ return false; -+ } -+ -+ if (required != 0) -+ rc = sepgsql_client_perms(rsid, tclass, required, auname, abort); -+ -+ ReleaseSysCache(tuple); -+ -+ if (!rc || relkind != RELKIND_RELATION) -+ return rc; -+ -+ /* -+ * Check column's permissions -+ */ -+ selColsEx = fixup_whole_row_reference(relOid, nattrs, -+ selectedCols); -+ modColsEx = fixup_whole_row_reference(relOid, nattrs, -+ modifiedCols); -+ columns = bms_union(selColsEx, modColsEx); -+ -+ while ((attno = bms_first_member(columns)) >= 0) -+ { -+ required = 0; -+ -+ if (bms_is_member(attno, selColsEx)) -+ required |= SEPG_DB_COLUMN__SELECT; -+ if (bms_is_member(attno, modColsEx)) -+ { -+ if (aclmask & ACL_UPDATE) -+ required |= SEPG_DB_COLUMN__UPDATE; -+ if (aclmask & ACL_INSERT) -+ required |= SEPG_DB_COLUMN__INSERT; -+ } -+ if (required == 0) -+ continue; -+ -+ attno += FirstLowInvalidHeapAttributeNumber; -+ rc = sepgsql_attribute_common(relOid, attno, required, abort); -+ if (!rc) -+ break; -+ } -+ -+ if (selColsEx != selectedCols) -+ bms_free(selColsEx); -+ if (modColsEx != modifiedCols) -+ bms_free(modColsEx); -+ bms_free(columns); -+ -+ return rc; -+ } -+#endif -+ return true; -+} -+ -+Oid * -+sepgsql_relation_create(const char *relName, -+ char relkind, -+ TupleDesc tupDesc, -+ Oid namespaceId, -+ List *supOids, -+ bool createAs) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t tsid; -+ sepgsql_sid_t csid; -+ Oid *secLabels; -+ uint16 tclass; -+ uint32 perms; -+ AttrNumber index, attno, nitems; -+ -+ /* -+ * The secLabeld array stores security identifiers to be -+ * assigned on the new table and columns. -+ * -+ * secLabels[0] is security-id of the relation. -+ * secLabels[attnum - FirstLowInvalidHeapAttributeNumber] -+ * is security-id of the columns. -+ */ -+ secLabels = seclabelMakeRelationDefaults(tupDesc, supOids); -+ nitems = tupDesc->natts - FirstLowInvalidHeapAttributeNumber; -+ -+ switch (relkind) -+ { -+ case RELKIND_RELATION: -+ if (!OidIsValid(secLabels[0])) -+ { -+ tsid = sepgsql_get_default_table_secid(namespaceId); -+ secLabels[0] = tsid.secid; -+ } -+ tclass = SEPG_CLASS_DB_TABLE; -+ perms = SEPG_DB_TABLE__CREATE; -+ if (createAs) -+ perms |= SEPG_DB_TABLE__INSERT; -+ break; -+ -+ case RELKIND_SEQUENCE: -+ if (!OidIsValid(secLabels[0])) -+ { -+ tsid = sepgsql_get_default_sequence_secid(namespaceId); -+ secLabels[0] = tsid.secid; -+ } -+ tclass = SEPG_CLASS_DB_SEQUENCE; -+ perms = SEPG_DB_SEQUENCE__CREATE; -+ break; -+ -+ case RELKIND_VIEW: -+ if (!OidIsValid(secLabels[0])) -+ { -+ tsid = sepgsql_get_default_view_secid(namespaceId); -+ secLabels[0] = tsid.secid; -+ } -+ tclass = SEPG_CLASS_DB_VIEW; -+ perms = SEPG_DB_VIEW__CREATE; -+ break; -+ -+ case RELKIND_COMPOSITE_TYPE: -+ if (!OidIsValid(secLabels[0])) -+ { -+ tsid = sepgsql_get_default_tuple_secid(TypeRelationId); -+ secLabels[0] = seclabelMoveSecid(RelationRelationId, -+ TypeRelationId, tsid.secid); -+ } -+ tclass = SEPG_CLASS_DB_TUPLE; -+ perms = SEPG_DB_TUPLE__INSERT; -+ break; -+ -+ default: -+ elog(ERROR, "Bug? unexpected relkind %c", relkind); -+ return NULL; -+ } -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_table:{create} or others */ -+ tsid.relid = RelationRelationId; -+ tsid.secid = secLabels[0]; -+ sepgsql_client_perms(tsid, tclass, perms, relName, true); -+ -+ /* no individual security-id except for RELKIND_RELATION */ -+ if (relkind != RELKIND_RELATION) -+ { -+ Oid securityId = seclabelMoveSecid(AttributeRelationId, -+ RelationRelationId, secLabels[0]); -+ -+ for (index = 1; index < nitems; index++) -+ secLabels[index] = securityId; -+ -+ return secLabels; -+ } -+ -+ /* -+ * security context of the columns -+ */ -+ for (index = 1; index < nitems; index++) -+ { -+ Form_pg_attribute attForm; -+ char auname[NAMEDATALEN * 2 + 10]; -+ -+ attno = index + FirstLowInvalidHeapAttributeNumber; -+ -+ /* skip unnecessary system columns */ -+ if ((attno == ObjectIdAttributeNumber && !tupDesc->tdhasoid) || -+ (attno == SecurityLabelAttributeNumber && !tupDesc->tdhassecid)) -+ continue; -+ -+ if (!OidIsValid(secLabels[index])) -+ { -+ csid = sepgsql_client_create_secid(tsid, -+ SEPG_CLASS_DB_COLUMN, -+ AttributeRelationId); -+ secLabels[index] = csid.secid; -+ } -+ -+ if (attno < 0) -+ attForm = SystemAttributeDefinition(attno, -+ tupDesc->tdhasoid, -+ tupDesc->tdhassecid); -+ else -+ attForm = tupDesc->attrs[attno]; -+ -+ /* db_column:{create (insert)} permission */ -+ csid.relid = AttributeRelationId; -+ csid.secid = secLabels[index]; -+ -+ perms = SEPG_DB_COLUMN__CREATE; -+ if (createAs && attno >= 0) -+ perms |= SEPG_DB_COLUMN__INSERT; -+ -+ snprintf(auname, sizeof(auname), "%s.%s", -+ relName, NameStr(attForm->attname)); -+ -+ sepgsql_client_perms(csid, -+ SEPG_CLASS_DB_COLUMN, -+ perms, -+ auname, -+ true); -+ } -+ return secLabels; -+ } -+#endif -+ return NULL; -+} -+ -+void -+sepgsql_relation_alter(Oid relationOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_relation_alter_schema(Oid relationOid, Oid newSchema) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_relation_alter_rename(Oid relationOid, const char *newName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_relation_alter_inherit(Oid childOid, Oid parentOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_relation_common(childOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_relation_relabel(Oid relationOid, char *new_label) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ char *auname; -+ char relkind; -+ uint16 tclass = 0; -+ -+ nsid.relid = RelationRelationId; -+ nsid.secid = seclabelTransInput(nsid.relid, new_label); -+ -+ auname = get_rel_name(relationOid); -+ relkind = get_rel_relkind(relationOid); -+ -+ switch (relkind) -+ { -+ case RELKIND_RELATION: -+ tclass = SEPG_CLASS_DB_TABLE; -+ break; -+ -+ case RELKIND_SEQUENCE: -+ tclass = SEPG_CLASS_DB_SEQUENCE; -+ break; -+ -+ case RELKIND_VIEW: -+ tclass = SEPG_CLASS_DB_VIEW; -+ break; -+ -+ case RELKIND_COMPOSITE_TYPE: -+ tclass = SEPG_CLASS_DB_TUPLE; -+ break; -+ -+ default: -+ elog(ERROR, "unexpected relkind %c", relkind); -+ break;; -+ } -+ /* db_xxx:{setattr relabelfrom} */ -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR | -+ SEPG_DB_TABLE__RELABELFROM, true); -+ -+ /* db_xxx:{relabelto} */ -+ sepgsql_client_perms(nsid, -+ tclass, -+ SEPG_DB_TABLE__RELABELTO, -+ auname, true); -+ pfree(auname); -+ -+ return nsid.secid; -+ } -+#endif -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL is not available"))); -+ return InvalidOid; -+} -+ -+void -+sepgsql_relation_drop(Oid relationOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__DROP, true); -+ } -+#endif -+} -+ -+void -+sepgsql_relation_getattr(Oid relationOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__GETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_relation_grant(Oid relationOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_relation_comment(Oid relationOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+bool -+sepgsql_relation_cluster(Oid relationOid, bool abort) -+{ -+#ifdef HAVE_SELINUX -+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); -+ -+ if (sepgsql_is_enabled()) -+ { -+ bool retval = -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__INDEXON, true); -+ return retval; -+ } -+#endif -+ return true; -+} -+ -+void -+sepgsql_relation_truncate(Relation rel) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); -+ -+ /* db_table:{delete} */ -+ sepgsql_relation_common(RelationGetRelid(rel), -+ SEPG_DB_TABLE__DELETE, true); -+ /* db_tuple:{delete} */ -+ } -+#endif -+} -+ -+void -+sepgsql_relation_lock(Relation rel) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); -+ -+ /* db_table:{lock} */ -+ sepgsql_relation_common(RelationGetRelid(rel), -+ SEPG_DB_TABLE__LOCK, true); -+ } -+#endif -+} -+ -+void -+sepgsql_relation_reindex(Oid relationOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); -+ -+ /* db_table:{indexon} */ -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__INDEXON, true); -+ } -+#endif -+} -+ -+void -+sepgsql_view_replace(Oid viewOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Assert(get_rel_relkind(viewOid) == RELKIND_VIEW); -+ /* db_view:{setattr} */ -+ sepgsql_relation_common(viewOid, -+ SEPG_DB_VIEW__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_index_create(Oid relationOid, Oid namespaceOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(namespaceOid, -+ SEPG_DB_SCHEMA__ADD_NAME, true); -+ /* db_table:{setattr indexon} */ -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR | -+ SEPG_DB_TABLE__INDEXON, true); -+ } -+#endif -+} -+ -+void -+sepgsql_index_reindex(Oid indexOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ HeapTuple tuple; -+ Oid relationOid; -+ -+ tuple = SearchSysCache1(INDEXRELID, indexOid); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for index %u", indexOid); -+ relationOid = ((Form_pg_index) GETSTRUCT(tuple))->indrelid; -+ ReleaseSysCache(tuple); -+ -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__INDEXON, true); -+ } -+#endif -+} -+ -+void -+sepgsql_sequence_get_value(Oid sequenceOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE); -+ sepgsql_relation_common(sequenceOid, -+ SEPG_DB_SEQUENCE__GET_VALUE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_sequence_next_value(Oid sequenceOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE); -+ sepgsql_relation_common(sequenceOid, -+ SEPG_DB_SEQUENCE__NEXT_VALUE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_sequence_set_value(Oid sequenceOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE); -+ sepgsql_relation_common(sequenceOid, -+ SEPG_DB_SEQUENCE__SET_VALUE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_rule_create(Oid relationOid, const char *ruleName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_rule_drop(Oid relationOid, const char *ruleName, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled() && !cascade) -+ { -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_rule_comment(Oid relationOid, const char *ruleName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_trigger_create(Oid relationOid, const char *triggerName, -+ Oid constrrelid, Oid funcOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); -+ -+ /* db_table:{setattr} */ -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ /* db_procedure:{install} */ -+ // sepgsql_procedure_common... -+ } -+#endif -+} -+ -+void -+sepgsql_trigger_alter(Oid relationOid, const char *triggerName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_trigger_drop(Oid relationOid, const char *triggerName, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled() && !cascade) -+ { -+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_trigger_comment(Oid relationOid, const char *triggerName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -+ -+void -+sepgsql_constraint_comment(Oid relationOid, const char *constName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); -+ sepgsql_relation_common(relationOid, -+ SEPG_DB_TABLE__SETATTR, true); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/role.c b/src/backend/sepgsql/role.c -new file mode 100644 -index 0000000..3c4891c ---- /dev/null -+++ b/src/backend/sepgsql/role.c -@@ -0,0 +1,142 @@ -+/* -+ * role.c -+ * -+ * SELinux hooks related to roles -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_authid.h" -+#include "catalog/pg_seclabel.h" -+#include "miscadmin.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/lsyscache.h" -+#include "utils/syscache.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_role_common(Oid roleOid, uint32 required, bool abort) -+{ -+ sepgsql_sid_t tsid; -+ HeapTuple tuple; -+ const char *auname; -+ bool retval; -+ -+ tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for role %u", roleOid); -+ -+ tsid.relid = AuthIdRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ auname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+#endif -+ -+Oid -+sepgsql_role_create(const char *roleName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ -+ nsid = sepgsql_get_default_tuple_secid(AuthIdRelationId); -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ roleName, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_role_alter(Oid roleOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_role_relabel(Oid roleOid, char *newLabel) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t tsid; -+ -+ tsid.relid = AuthIdRelationId; -+ tsid.secid = seclabelTransInput(tsid.relid, newLabel); -+ -+ /* db_tuple:{update relabelfrom} */ -+ sepgsql_role_common(roleOid, -+ SEPG_DB_TUPLE__UPDATE | -+ SEPG_DB_TUPLE__RELABELFROM, -+ true); -+ -+ /* db_tuple:{relabelto} */ -+ sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__RELABELTO, -+ GetUserNameFromId(roleOid), -+ true); -+ return tsid.secid; -+ } -+#endif -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL is not available"))); -+ return InvalidOid; -+} -+ -+void -+sepgsql_role_drop(Oid roleOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_role_grant(Oid roleOid, bool is_grant, List *memberIds) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_role_comment(Oid roleOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/rowlv.c b/src/backend/sepgsql/rowlv.c -new file mode 100644 -index 0000000..892daf9 ---- /dev/null -+++ b/src/backend/sepgsql/rowlv.c -@@ -0,0 +1,346 @@ -+/* -+ * rowlv.c -+ * -+ * Row-level access control facilities -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "access/heapam.h" -+#include "access/sysattr.h" -+#include "catalog/pg_attribute.h" -+#include "catalog/pg_class.h" -+#include "catalog/pg_database.h" -+#include "catalog/pg_language.h" -+#include "catalog/pg_largeobject_metadata.h" -+#include "catalog/pg_namespace.h" -+#include "catalog/pg_proc.h" -+#include "catalog/pg_seclabel.h" -+#include "catalog/pg_type.h" -+#include "nodes/makefuncs.h" -+#include "parser/parsetree.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "storage/bufmgr.h" -+#include "utils/fmgroids.h" -+#include "utils/lsyscache.h" -+#include "utils/syscache.h" -+#include "utils/tqual.h" -+ -+static int sepgsql_rowlv_mode = SEPGSQL_ROWLV_FILTER; -+ -+int -+sepgsql_rowlv_get_mode(void) -+{ -+ return sepgsql_rowlv_mode; -+} -+ -+int -+sepgsql_rowlv_set_mode(int new_mode) -+{ -+ int old_mode = sepgsql_rowlv_mode; -+ -+ Assert(new_mode == SEPGSQL_ROWLV_FILTER || -+ new_mode == SEPGSQL_ROWLV_ABORT || -+ new_mode == SEPGSQL_ROWLV_BYPASS); -+ -+ sepgsql_rowlv_mode = new_mode; -+ -+ return old_mode; -+} -+ -+uint32 -+sepgsql_rowlv_permissions(RangeTblEntry *rte) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid relOid = rte->relid; -+ HeapTuple reltup; -+ bool relhassecids; -+ uint32 required = 0; -+ -+ if (!OidIsValid(relOid)) -+ return 0; -+ -+ if (get_rel_relkind(relOid) != RELKIND_RELATION) -+ return 0; -+ -+ reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); -+ if (!HeapTupleIsValid(reltup)) -+ elog(ERROR, "cache lookup failed for relation %u", relOid); -+ relhassecids = ((Form_pg_class) GETSTRUCT(reltup))->relhassecids; -+ ReleaseSysCache(reltup); -+ -+ if (!relhassecids) -+ return 0; -+ -+ if (rte->requiredPerms & ACL_SELECT) -+ required |= SEPG_DB_TUPLE__SELECT; -+ -+ if (rte->requiredPerms & ACL_UPDATE && -+ !bms_is_empty(rte->modifiedCols)) -+ required |= SEPG_DB_TUPLE__UPDATE; -+ -+ if (rte->requiredPerms & ACL_DELETE) -+ required |= SEPG_DB_TUPLE__DELETE; -+ -+ return required; -+ } -+#endif -+ return 0; -+} -+ -+#ifdef HAVE_SELINUX -+static bool -+sepgsql_tuple_perms(Relation rel, HeapTuple tuple, uint32 required, bool abort) -+{ -+ Form_pg_class relForm; -+ Form_pg_attribute attForm; -+ Form_pg_type typForm; -+ sepgsql_sid_t tsid; -+ uint16 tclass; -+ -+ /* object class? */ -+ switch (RelationGetRelid(rel)) -+ { -+ case DatabaseRelationId: -+ tclass = SEPG_CLASS_DB_DATABASE; -+ break; -+ -+ case NamespaceRelationId: -+ tclass = SEPG_CLASS_DB_SCHEMA; -+ break; -+ -+ case RelationRelationId: -+ relForm = (Form_pg_class) GETSTRUCT(tuple); -+ switch (relForm->relkind) -+ { -+ case RELKIND_RELATION: -+ tclass = SEPG_CLASS_DB_TABLE; -+ break; -+ case RELKIND_SEQUENCE: -+ tclass = SEPG_CLASS_DB_SEQUENCE; -+ break; -+ case RELKIND_VIEW: -+ tclass = SEPG_CLASS_DB_VIEW; -+ break; -+ case RELKIND_COMPOSITE_TYPE: -+ tclass = SEPG_CLASS_DB_TUPLE; -+ break; -+ default: /* index, toast */ -+ tclass = SEPG_CLASS_DB_TABLE; -+ break; -+ } -+ break; -+ -+ case AttributeRelationId: -+ attForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ switch (get_rel_relkind(attForm->attrelid)) -+ { -+ case RELKIND_RELATION: -+ tclass = SEPG_CLASS_DB_COLUMN; -+ break; -+ case RELKIND_SEQUENCE: -+ tclass = SEPG_CLASS_DB_SEQUENCE; -+ break; -+ case RELKIND_VIEW: -+ tclass = SEPG_CLASS_DB_VIEW; -+ break; -+ case RELKIND_COMPOSITE_TYPE: -+ tclass = SEPG_CLASS_DB_TUPLE; -+ break; -+ case RELKIND_INDEX: -+ case RELKIND_TOASTVALUE: -+ tclass = SEPG_CLASS_DB_TABLE; -+ break; -+ default: -+ /* orphan attribute? */ -+ return !sepgsql_get_enforce(); -+ } -+ break; -+ -+ case ProcedureRelationId: -+ tclass = SEPG_CLASS_DB_PROCEDURE; -+ break; -+ -+ case LanguageRelationId: -+ tclass = SEPG_CLASS_DB_LANGUAGE; -+ break; -+ -+ case LargeObjectMetadataRelationId: -+ tclass = SEPG_CLASS_DB_BLOB; -+ break; -+ -+ case TypeRelationId: -+ typForm = (Form_pg_type) GETSTRUCT(tuple); -+ if (!OidIsValid(typForm->typrelid)) -+ tclass = SEPG_CLASS_DB_TUPLE; -+ else -+ { -+ switch (get_rel_relkind(typForm->typrelid)) -+ { -+ case RELKIND_RELATION: -+ case RELKIND_INDEX: -+ case RELKIND_TOASTVALUE: -+ tclass = SEPG_CLASS_DB_TABLE; -+ break; -+ case RELKIND_SEQUENCE: -+ tclass = SEPG_CLASS_DB_SEQUENCE; -+ break; -+ case RELKIND_VIEW: -+ tclass = SEPG_CLASS_DB_VIEW; -+ break; -+ case RELKIND_COMPOSITE_TYPE: -+ tclass = SEPG_CLASS_DB_TUPLE; -+ break; -+ default: -+ /* orphan type? */ -+ return !sepgsql_get_enforce(); -+ } -+ } -+ break; -+ -+ default: -+ tclass = SEPG_CLASS_DB_TUPLE; -+ break; -+ } -+ -+ /* do permission check */ -+ tsid.relid = RelationGetRelid(rel); -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ return sepgsql_client_perms(tsid, tclass, required, NULL, abort); -+} -+#endif -+ -+bool -+sepgsql_rowlv_quals(Relation rel, uint32 required, -+ TupleTableSlot *slot, bool abort) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ HeapTuple tuple; -+ -+ /* no permission to be checked */ -+ if (!required) -+ return true; -+ -+ /* mismatch invocation for the current rowlv mode */ -+ if ((!abort && sepgsql_rowlv_mode != SEPGSQL_ROWLV_FILTER) || -+ (abort && sepgsql_rowlv_mode != SEPGSQL_ROWLV_ABORT)) -+ return true; -+ -+ Assert(OidIsValid(RelationGetRelid(rel))); -+ -+ tuple = ExecMaterializeSlot(slot); -+ -+ return sepgsql_tuple_perms(rel, tuple, required, abort); -+ } -+#endif -+ return true; -+} -+ -+bool -+sepgsql_rowlv_copyto(Relation rel, HeapTuple tuple) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ return sepgsql_tuple_perms(rel, tuple, SEPG_DB_TUPLE__SELECT, false); -+ } -+#endif -+ return true; -+} -+ -+void -+sepgsql_tuple_insert(Relation rel, HeapTuple tuple) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ Oid relOid = RelationGetRelid(rel); -+ -+ if (!RelationGetForm(rel)->relhassecids) -+ { -+ nsid.relid = RelationRelationId; -+ nsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); -+ } -+ else if (OidIsValid(HeapTupleGetSecid(tuple))) -+ { -+ nsid.relid = relOid; -+ nsid.secid = HeapTupleGetSecid(tuple); -+ } -+ else -+ { -+ nsid = sepgsql_get_default_tuple_secid(relOid); -+ HeapTupleSetSecid(tuple, nsid.secid); -+ } -+ /* db_tuple:{insert} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ NULL, -+ true); -+ } -+#endif -+} -+ -+void -+sepgsql_tuple_update(Relation rel, ItemPointer otid, HeapTuple newtup) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t tsid; -+ HeapTupleData oldtup; -+ Buffer oldbuf; -+ Oid newSecId = HeapTupleGetSecid(newtup); -+ Oid oldSecId; -+ -+ /* -+ * heap_update() preserves security id of the original tuple, -+ * if no explicit security label was given, so we don't need -+ * to check anything. -+ * At this point, db_tuple:{update} is already checked. -+ */ -+ if (!OidIsValid(newSecId)) -+ return; -+ -+ /* -+ * User gave an explicit security label -+ */ -+ ItemPointerCopy(otid, &oldtup.t_self); -+ if (!heap_fetch(rel, SnapshotAny, &oldtup, &oldbuf, false, NULL)) -+ elog(ERROR, "failed to fetch old version of the tuple"); -+ -+ tsid.relid = RelationGetRelid(rel); -+ oldSecId = HeapTupleGetSecid(&oldtup); -+ -+ if (!seclabelCompareSecid(tsid.relid, oldSecId, -+ tsid.relid, newSecId)) -+ { -+ /* db_tuple:{relabelfrom} */ -+ tsid.secid = oldSecId; -+ sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__RELABELFROM, -+ NULL, -+ true); -+ -+ /* db_tuple:{relabelto} */ -+ tsid.secid = newSecId; -+ sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__RELABELTO, -+ NULL, -+ true); -+ } -+ ReleaseBuffer(oldbuf); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/schema.c b/src/backend/sepgsql/schema.c -new file mode 100644 -index 0000000..24878f9 ---- /dev/null -+++ b/src/backend/sepgsql/schema.c -@@ -0,0 +1,173 @@ -+/* -+ * schema.c -+ * -+ * SELinux hooks related to schema -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_namespace.h" -+#include "catalog/pg_seclabel.h" -+#include "miscadmin.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/lsyscache.h" -+#include "utils/syscache.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_schema_common(Oid namespaceOid, uint32 required, bool abort) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t tsid; -+ const char *auname; -+ bool retval; -+ -+ tuple = SearchSysCache1(NAMESPACEOID, -+ ObjectIdGetDatum(namespaceOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for namespace %u", namespaceOid); -+ -+ tsid.relid = NamespaceRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ auname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_SCHEMA, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+#endif -+ -+Oid -+sepgsql_schema_create(const char *nspName, bool is_temp) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ -+ /* compute default security context */ -+ nsid = sepgsql_get_default_schema_secid(MyDatabaseId); -+ -+ /* db_schema:{create} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_SCHEMA, -+ SEPG_DB_SCHEMA__CREATE, -+ nspName, true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_schema_alter(Oid namespaceOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_schema_common(namespaceOid, -+ SEPG_DB_SCHEMA__SETATTR, -+ true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_schema_relabel(Oid namespaceOid, char *new_label) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ char *auname; -+ -+ nsid.relid = NamespaceRelationId; -+ nsid.secid = seclabelTransInput(nsid.relid, new_label); -+ -+ auname = get_namespace_name(namespaceOid); -+ -+ /* db_schema:{setattr relabelfrom} */ -+ sepgsql_schema_common(namespaceOid, -+ SEPG_DB_SCHEMA__SETATTR | -+ SEPG_DB_SCHEMA__RELABELFROM, -+ true); -+ -+ /* db_schema:{relabelto} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_SCHEMA, -+ SEPG_DB_SCHEMA__RELABELTO, -+ auname, -+ true); -+ pfree(auname); -+ -+ return nsid.secid; -+ } -+#endif -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL is not available"))); -+ return InvalidOid; -+} -+ -+void -+sepgsql_schema_drop(Oid namespaceOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_schema_common(namespaceOid, -+ SEPG_DB_SCHEMA__DROP, -+ true); -+ } -+#endif -+} -+ -+void -+sepgsql_schema_grant(Oid namespaceOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_schema_common(namespaceOid, -+ SEPG_DB_SCHEMA__SETATTR, -+ true); -+ } -+#endif -+} -+ -+bool -+sepgsql_schema_search(Oid namespaceOid, bool abort) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ bool retval = -+ sepgsql_schema_common(namespaceOid, -+ SEPG_DB_SCHEMA__SEARCH, -+ abort); -+ return retval; -+ } -+#endif -+ return true; -+} -+ -+void -+sepgsql_schema_comment(Oid namespaceOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_schema_common(namespaceOid, -+ SEPG_DB_SCHEMA__SETATTR, -+ true); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/selinux.c b/src/backend/sepgsql/selinux.c -new file mode 100644 -index 0000000..5e40cf9 ---- /dev/null -+++ b/src/backend/sepgsql/selinux.c -@@ -0,0 +1,690 @@ -+/* -+ * src/backend/security/sepgsql/selinux.c -+ * Routines to communicate with SELinux. -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_seclabel.h" -+#include "libpq/libpq.h" -+#include "miscadmin.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/guc.h" -+#include "utils/memutils.h" -+ -+#include -+ -+/* -+ * selinux_catalog -+ * -+ * This static translation lookup table enables to associate a certain -+ * object class/permission name with its internal code, such as -+ * SEPG_CLASS_DB_SCHEMA. -+ * -+ * SELinux requires applications to represent object class and a set of -+ * permissions in code, instead of its name, when we ask SELinux's decision. -+ * -+ * See the definition of security_compute_av(3) API in libselinux. -+ * We need to gives a code of object class, and interpret what permissions -+ * are allowed on the object class from av_decision structure. -+ * Actual values of the code depend on the security policy. In other words, -+ * we cannot know what number is assigned on a certain object class and -+ * permissions. -+ * The string_to_security_class(3) and string_to_av_perm(3) APIs takes -+ * arguments with the name of object class/permission, and returns the -+ * code for the given object class/permissions. -+ * For example, we can know what code is assigned on the "db_table" class -+ * using these functions as follows: -+ * -+ * uint16 tclass_ex = string_to_security_class("db_table"); -+ * -+ * On the other hand, we use an alternative code internally to simplify -+ * the implementation, such as SEPG_CLASS_* for object class. -+ * The following selinux_catalog is used to translate the 'internal' -+ * code and the 'external' code. -+ * -+ * It allows to lookup name of the object class or permission corresponding -+ * to a certain 'internal' code. Then, we can give the name to SELinux's -+ * API to obtain 'external' code which can be used to ask in-kernel SELinux. -+ */ -+static struct -+{ -+ const char *class_name; -+ uint16 class_code; -+ struct -+ { -+ const char *perm_name; -+ uint32 perm_code; -+ } perms[32]; -+} selinux_catalog[] = { -+ { -+ "process", SEPG_CLASS_PROCESS, -+ { -+ { "translation", SEPG_PROCESS__TRANSITION }, -+ { NULL, 0UL } -+ } -+ }, -+ { -+ "file", SEPG_CLASS_FILE, -+ { -+ { "read", SEPG_FILE__READ }, -+ { "write", SEPG_FILE__WRITE }, -+ { "create", SEPG_FILE__CREATE }, -+ { "getattr", SEPG_FILE__GETATTR }, -+ { "unlink", SEPG_FILE__UNLINK }, -+ { "rename", SEPG_FILE__RENAME }, -+ { "append", SEPG_FILE__APPEND }, -+ { NULL, 0UL } -+ } -+ }, -+ { -+ "dir", SEPG_CLASS_DIR, -+ { -+ { "read", SEPG_DIR__READ }, -+ { "write", SEPG_DIR__WRITE }, -+ { "create", SEPG_DIR__CREATE }, -+ { "getattr", SEPG_DIR__GETATTR }, -+ { "unlink", SEPG_DIR__UNLINK }, -+ { "rename", SEPG_DIR__RENAME }, -+ { "search", SEPG_DIR__SEARCH }, -+ { "add_name", SEPG_DIR__ADD_NAME }, -+ { "remove_name", SEPG_DIR__REMOVE_NAME }, -+ { "rmdir", SEPG_DIR__RMDIR }, -+ { "reparent", SEPG_DIR__REPARENT }, -+ { NULL, 0UL } -+ } -+ }, -+ { -+ "lnk_file", SEPG_CLASS_LNK_FILE, -+ { -+ { "read", SEPG_LNK_FILE__READ }, -+ { "write", SEPG_LNK_FILE__WRITE }, -+ { "create", SEPG_LNK_FILE__CREATE }, -+ { "getattr", SEPG_LNK_FILE__GETATTR }, -+ { "unlink", SEPG_LNK_FILE__UNLINK }, -+ { "rename", SEPG_LNK_FILE__RENAME }, -+ { NULL, 0UL } -+ } -+ }, -+ { -+ "chr_file", SEPG_CLASS_CHR_FILE, -+ { -+ { "read", SEPG_CHR_FILE__READ }, -+ { "write", SEPG_CHR_FILE__WRITE }, -+ { "create", SEPG_CHR_FILE__CREATE }, -+ { "getattr", SEPG_CHR_FILE__GETATTR }, -+ { "unlink", SEPG_CHR_FILE__UNLINK }, -+ { "rename", SEPG_CHR_FILE__RENAME }, -+ { NULL, 0UL } -+ } -+ }, -+ { -+ "blk_file", SEPG_CLASS_BLK_FILE, -+ { -+ { "read", SEPG_BLK_FILE__READ }, -+ { "write", SEPG_BLK_FILE__WRITE }, -+ { "create", SEPG_BLK_FILE__CREATE }, -+ { "getattr", SEPG_BLK_FILE__GETATTR }, -+ { "unlink", SEPG_BLK_FILE__UNLINK }, -+ { "rename", SEPG_BLK_FILE__RENAME }, -+ { NULL, 0UL } -+ } -+ }, -+ { -+ "sock_file", SEPG_CLASS_SOCK_FILE, -+ { -+ { "read", SEPG_SOCK_FILE__READ }, -+ { "write", SEPG_SOCK_FILE__WRITE }, -+ { "create", SEPG_SOCK_FILE__CREATE }, -+ { "getattr", SEPG_SOCK_FILE__GETATTR }, -+ { "unlink", SEPG_SOCK_FILE__UNLINK }, -+ { "rename", SEPG_SOCK_FILE__RENAME }, -+ { NULL, 0UL } -+ } -+ }, -+ { -+ "fifo_file", SEPG_CLASS_FIFO_FILE, -+ { -+ { "read", SEPG_FIFO_FILE__READ }, -+ { "write", SEPG_FIFO_FILE__WRITE }, -+ { "create", SEPG_FIFO_FILE__CREATE }, -+ { "getattr", SEPG_FIFO_FILE__GETATTR }, -+ { "unlink", SEPG_FIFO_FILE__UNLINK }, -+ { "rename", SEPG_FIFO_FILE__RENAME }, -+ { NULL, 0UL } -+ } -+ }, -+ { -+ "db_database", SEPG_CLASS_DB_DATABASE, -+ { -+ { "create", SEPG_DB_DATABASE__CREATE }, -+ { "drop", SEPG_DB_DATABASE__DROP }, -+ { "getattr", SEPG_DB_DATABASE__GETATTR }, -+ { "setattr", SEPG_DB_DATABASE__SETATTR }, -+ { "relabelfrom", SEPG_DB_DATABASE__RELABELFROM }, -+ { "relabelto", SEPG_DB_DATABASE__RELABELTO }, -+ { "access", SEPG_DB_DATABASE__ACCESS }, -+ { "load_module", SEPG_DB_DATABASE__LOAD_MODULE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_schema", SEPG_CLASS_DB_SCHEMA, -+ { -+ { "create", SEPG_DB_SCHEMA__CREATE }, -+ { "drop", SEPG_DB_SCHEMA__DROP }, -+ { "getattr", SEPG_DB_SCHEMA__GETATTR }, -+ { "setattr", SEPG_DB_SCHEMA__SETATTR }, -+ { "relabelfrom", SEPG_DB_SCHEMA__RELABELFROM }, -+ { "relabelto", SEPG_DB_SCHEMA__RELABELTO }, -+ { "search", SEPG_DB_SCHEMA__SEARCH }, -+ { "add_name", SEPG_DB_SCHEMA__ADD_NAME }, -+ { "remove_name", SEPG_DB_SCHEMA__REMOVE_NAME }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_table", SEPG_CLASS_DB_TABLE, -+ { -+ { "create", SEPG_DB_TABLE__CREATE }, -+ { "drop", SEPG_DB_TABLE__DROP }, -+ { "getattr", SEPG_DB_TABLE__GETATTR }, -+ { "setattr", SEPG_DB_TABLE__SETATTR }, -+ { "relabelfrom", SEPG_DB_TABLE__RELABELFROM }, -+ { "relabelto", SEPG_DB_TABLE__RELABELTO }, -+ { "select", SEPG_DB_TABLE__SELECT }, -+ { "update", SEPG_DB_TABLE__UPDATE }, -+ { "insert", SEPG_DB_TABLE__INSERT }, -+ { "delete", SEPG_DB_TABLE__DELETE }, -+ { "lock", SEPG_DB_TABLE__LOCK }, -+ { "indexon", SEPG_DB_TABLE__INDEXON }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_view", SEPG_CLASS_DB_VIEW, -+ { -+ { "create", SEPG_DB_VIEW__CREATE }, -+ { "drop", SEPG_DB_VIEW__DROP }, -+ { "getattr", SEPG_DB_VIEW__GETATTR }, -+ { "setattr", SEPG_DB_VIEW__SETATTR }, -+ { "relabelfrom", SEPG_DB_VIEW__RELABELFROM }, -+ { "relabelto", SEPG_DB_VIEW__RELABELTO }, -+ { "expand", SEPG_DB_VIEW__EXPAND }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_sequence", SEPG_CLASS_DB_SEQUENCE, -+ { -+ { "create", SEPG_DB_SEQUENCE__CREATE }, -+ { "drop", SEPG_DB_SEQUENCE__DROP }, -+ { "getattr", SEPG_DB_SEQUENCE__GETATTR }, -+ { "setattr", SEPG_DB_SEQUENCE__SETATTR }, -+ { "relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM }, -+ { "relabelto", SEPG_DB_SEQUENCE__RELABELTO }, -+ { "get_value", SEPG_DB_SEQUENCE__GET_VALUE }, -+ { "next_value", SEPG_DB_SEQUENCE__NEXT_VALUE }, -+ { "set_value", SEPG_DB_SEQUENCE__SET_VALUE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_procedure", SEPG_CLASS_DB_PROCEDURE, -+ { -+ { "create", SEPG_DB_PROCEDURE__CREATE }, -+ { "drop", SEPG_DB_PROCEDURE__DROP }, -+ { "getattr", SEPG_DB_PROCEDURE__GETATTR }, -+ { "setattr", SEPG_DB_PROCEDURE__SETATTR }, -+ { "relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM }, -+ { "relabelto", SEPG_DB_PROCEDURE__RELABELTO }, -+ { "execute", SEPG_DB_PROCEDURE__EXECUTE }, -+ { "entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT }, -+ { "install", SEPG_DB_PROCEDURE__INSTALL }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_column", SEPG_CLASS_DB_COLUMN, -+ { -+ { "create", SEPG_DB_COLUMN__CREATE }, -+ { "drop", SEPG_DB_COLUMN__DROP }, -+ { "getattr", SEPG_DB_COLUMN__GETATTR }, -+ { "setattr", SEPG_DB_COLUMN__SETATTR }, -+ { "relabelfrom", SEPG_DB_COLUMN__RELABELFROM }, -+ { "relabelto", SEPG_DB_COLUMN__RELABELTO }, -+ { "select", SEPG_DB_COLUMN__SELECT }, -+ { "update", SEPG_DB_COLUMN__UPDATE }, -+ { "insert", SEPG_DB_COLUMN__INSERT }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_tuple", SEPG_CLASS_DB_TUPLE, -+ { -+ { "relabelfrom", SEPG_DB_TUPLE__RELABELFROM }, -+ { "relabelto", SEPG_DB_TUPLE__RELABELTO }, -+ { "select", SEPG_DB_TUPLE__SELECT }, -+ { "update", SEPG_DB_TUPLE__UPDATE }, -+ { "insert", SEPG_DB_TUPLE__INSERT }, -+ { "delete", SEPG_DB_TUPLE__DELETE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_blob", SEPG_CLASS_DB_BLOB, -+ { -+ { "create", SEPG_DB_BLOB__CREATE }, -+ { "drop", SEPG_DB_BLOB__DROP }, -+ { "getattr", SEPG_DB_BLOB__GETATTR }, -+ { "setattr", SEPG_DB_BLOB__SETATTR }, -+ { "relabelfrom", SEPG_DB_BLOB__RELABELFROM }, -+ { "relabelto", SEPG_DB_BLOB__RELABELTO }, -+ { "read", SEPG_DB_BLOB__READ }, -+ { "write", SEPG_DB_BLOB__WRITE }, -+ { "import", SEPG_DB_BLOB__IMPORT }, -+ { "export", SEPG_DB_BLOB__EXPORT }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_language", SEPG_CLASS_DB_LANGUAGE, -+ { -+ { "create", SEPG_DB_LANGUAGE__CREATE }, -+ { "drop", SEPG_DB_LANGUAGE__DROP }, -+ { "getattr", SEPG_DB_LANGUAGE__GETATTR }, -+ { "setattr", SEPG_DB_LANGUAGE__SETATTR }, -+ { "relabelfrom", SEPG_DB_LANGUAGE__RELABELFROM }, -+ { "relabelto", SEPG_DB_LANGUAGE__RELABELTO }, -+ { "implement", SEPG_DB_LANGUAGE__IMPLEMENTE }, -+ { "execute", SEPG_DB_LANGUAGE__EXECUTE }, -+ { NULL, 0UL }, -+ } -+ }, -+}; -+ -+/* -+ * GUC option: sepostgresql = [default|enforcing|permissive|disabled] -+ * -+ * SEPGSQL_MODE_DEFAULT : It follows system setting -+ * SEPGSQL_MODE_ENFORCING : Use enforcing mode always -+ * SEPGSQL_MODE_PERMISSIVE : Use permissive mode always -+ * SEPGSQL_MODE_INTERNAL : Internally used mode. Same as permissive mode -+ * except for silence in audit logs -+ * SEPGSQL_MODE_DISABLED : It always disables SE-PgSQL configuration -+ */ -+int sepostgresql_mode; -+ -+/* -+ * sepgsql_is_enabled -+ * -+ * If it returns true, SE-PgSQL is enabled. Otherwise, it is disabled. -+ */ -+bool -+sepgsql_is_enabled(void) -+{ -+ static int enabled = -1; -+ -+ /* -+ * If sepostgresql = disabled, it always returns FALSE -+ * independently from the system status. -+ */ -+ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED) -+ return false; -+ -+ /* -+ * SE-PgSQL needs SELinux is enabled on the operating system. -+ * If it is disabled, SE-PgSQL has to be also disabled, even if -+ * 'enforcing' or 'permissive' are specified. -+ */ -+ if (enabled < 0) -+ enabled = is_selinux_enabled(); -+ -+ return enabled > 0 ? true : false; -+} -+ -+/* -+ * sepgsql_get_enforce -+ * -+ * It returns true, if SE-PgSQL performs in enforcing mode. -+ * -+ * In enforcing mode, SE-PgSQL performs as expected. It checks permissions -+ * on the required action, and it prevents them if violated. -+ * In permissive mode, SE-PgSQL also checks permissions, but it does not -+ * prevent anything, even if violated. It generates audit logs for access -+ * violations, so we can use this mode to debug security policy itself. -+ */ -+bool -+sepgsql_get_enforce(void) -+{ -+ if (sepostgresql_mode == SEPGSQL_MODE_DEFAULT) -+ { -+ if (security_getenforce() == 1) -+ return true; -+ } -+ else if (sepostgresql_mode == SEPGSQL_MODE_ENFORCING) -+ return true; -+ -+ return false; -+} -+ -+/* -+ * sepgsql_show_mode -+ * -+ * It returns the current performing mode ('selinux_support') -+ * in human readable form. -+ */ -+const char * -+sepgsql_show_mode(void) -+{ -+ if (!sepgsql_is_enabled()) -+ return "disabled"; -+ -+ if (!sepgsql_get_enforce()) -+ return "permissive"; -+ -+ return "enforcing"; -+} -+ -+/* -+ * GUC parameter to turn on/off debuging audit generation -+ */ -+bool sepgsql_debug_audit; -+ -+/* -+ * sepgsql_audit_log -+ * -+ * It generates a security audit record. In the default, it writes out -+ * audit records into standard PG's logfile. It also allows to set up -+ * external audit log receiver, such as auditd in Linux, using the -+ * sepgsql_audit_hook. -+ * -+ * SELinux can control what should be audited and should not using -+ * "auditdeny" and "auditallow" rules in the security policy. In the -+ * default, all the access violations are audited, and all the access -+ * allowed are not audited. But we can set up the security policy, so -+ * we can have exceptions. So, it is necessary to follow the suggestion -+ * come from the security policy. (av_decision.auditallow and auditdeny) -+ * -+ * Security audit is an important feature, because it enables us to check -+ * what was happen if we have a security incident. In fact, ISO/IEC15408 -+ * defines several security functionalities for audit features. -+ */ -+void -+sepgsql_audit_log(bool denied, char *scontext, char *tcontext, -+ uint16 tclass, uint32 audited, const char *audit_name) -+{ -+ StringInfoData buf; -+ const char *tclass_name; -+ const char *perm_name; -+ int level = LOG; -+ int i; -+ -+ /* -+ * translation of security contexts to human readable format, -+ * if sepgsql_mcstrans is turned on. -+ */ -+ scontext = sepgsql_mcstrans_out(scontext); -+ tcontext = sepgsql_mcstrans_out(tcontext); -+ -+ /* lookup name of the object class */ -+ tclass_name = selinux_catalog[tclass].class_name; -+ -+ /* lookup name of the permissions */ -+ initStringInfo(&buf); -+ appendStringInfo(&buf, "{"); -+ -+ for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++) -+ { -+ if (audited & (1UL << i)) -+ { -+ perm_name = selinux_catalog[tclass].perms[i].perm_name; -+ appendStringInfo(&buf, " %s", perm_name); -+ } -+ } -+ appendStringInfo(&buf, " }"); -+ -+ /* -+ * Call external audit module, if loaded -+ */ -+ appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s", -+ scontext, tcontext, tclass_name); -+ if (audit_name) -+ appendStringInfo(&buf, " name=%s", audit_name); -+ -+ if (sepgsql_debug_audit) -+ level = client_min_messages; -+ -+ ereport(level, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("SELinux: %s %s", -+ (denied ? "denied" : "allowed"), buf.data))); -+} -+ -+/* -+ * sepgsql_compute_avd -+ * -+ * It actually asks SELinux what permissions are allowed on a pair of -+ * the security contexts and object class. It also returns what permissions -+ * should be audited on access violation or allowed. -+ * In most cases, subject's security context (scontext) is a client, and -+ * target security context (tcontext) is a database object. -+ * -+ * The access control decision shall be set on the given av_decision. -+ * The av_decision.allowed has a bitmask of SEPG___ -+ * to suggest a set of allowed actions in this object class. -+ */ -+void -+sepgsql_compute_avd(char *scontext, char *tcontext, -+ uint16 tclass, struct av_decision *avd) -+{ -+ const char *tclass_name; -+ security_class_t tclass_ex; -+ struct av_decision avd_ex; -+ int i, deny_unknown = security_deny_unknown(); -+ -+ /* Get external code of the object class*/ -+ Assert(tclass < SEPG_CLASS_MAX); -+ -+ tclass_name = selinux_catalog[tclass].class_name; -+ tclass_ex = string_to_security_class(tclass_name); -+ -+ if (tclass_ex == 0) -+ { -+ /* -+ * If the current security policy does not support permissions -+ * corresponding to database objects, we fill up them with dummy -+ * data. -+ * If security_deny_unknown() returns positive value, undefined -+ * permissions should be denied. Otherwise, allowed -+ */ -+ avd->allowed = (deny_unknown > 0 ? 0 : ~0U); -+ avd->auditallow = 0U; -+ avd->auditdeny = ~0U; -+ avd->flags = 0; -+ -+ return; -+ } -+ -+ /* -+ * Ask SELinux what is allowed set of permissions on a pair of the -+ * security contexts and the given object class. -+ */ -+ if (security_compute_av_flags_raw(scontext, tcontext, -+ tclass_ex, 0, &avd_ex) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("SELinux could not compute av_decision: " -+ "scontext=%s tcontext=%s tclass=%s", -+ scontext, tcontext, tclass_name))); -+ -+ /* -+ * SELinux returns its access control decision as a set of permissions -+ * represented in external code which depends on run-time environment. -+ * So, we need to translate it to the internal representation before -+ * returning results for the caller. -+ */ -+ memset(avd, 0, sizeof(struct av_decision)); -+ -+ for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++) -+ { -+ access_vector_t perm_code_ex; -+ const char *perm_name = selinux_catalog[tclass].perms[i].perm_name; -+ uint32 perm_code = selinux_catalog[tclass].perms[i].perm_code; -+ -+ perm_code_ex = string_to_av_perm(tclass_ex, perm_name); -+ if (perm_code_ex == 0) -+ { -+ /* fill up undefined permissions */ -+ if (!deny_unknown) -+ avd->allowed |= perm_code; -+ avd->auditdeny |= perm_code; -+ -+ continue; -+ } -+ -+ if (avd_ex.allowed & perm_code_ex) -+ avd->allowed |= perm_code; -+ if (avd_ex.auditallow & perm_code_ex) -+ avd->auditallow |= perm_code; -+ if (avd_ex.auditdeny & perm_code_ex) -+ avd->auditdeny |= perm_code; -+ } -+ -+ return; -+} -+ -+/* -+ * sepgsql_compute_perms -+ * -+ * It makes access control decision communicating with SELinux. -+ * If SELinux does not allow required permissions on a pair of the security -+ * contexts, it raises an error or returns false. -+ * -+ * scontext : The security context of subject. In most cases, it is client. -+ * tcontext : The security context of target database object. -+ * tclass : One of the object class code (SEPG_CLASS_*) declared in the -+ * header file. -+ * required : A bitmap of the required permissions (SEPG___) -+ * declared in the header file. -+ * audit_name : A human readable name of the database object for auditing. -+ * abort : True, if caller want to raise an error on access violation. -+ */ -+bool -+sepgsql_compute_perms(char *scontext, char *tcontext, -+ uint16 tclass, uint32 required, -+ const char *audit_name, bool abort) -+{ -+ struct av_decision avd; -+ uint32 denied; -+ uint32 audited; -+ -+ sepgsql_compute_avd(scontext, tcontext, tclass, &avd); -+ -+ /* -+ * It logs a security audit record for the given request, if necessary. -+ * When SE-PgSQL performs 'internal' mode, it needs to keep silent. -+ */ -+ denied = required & ~avd.allowed; -+ if (sepgsql_debug_audit && tclass != SEPG_CLASS_DB_TUPLE) -+ audited = (denied ? (denied & ~0) : (required & ~0)); -+ else -+ audited = (denied ? (denied & avd.auditdeny) -+ : (required & avd.auditallow)); -+ -+ if (audited && sepostgresql_mode != SEPGSQL_MODE_INTERNAL) -+ { -+ sepgsql_audit_log(!!denied, scontext, tcontext, -+ tclass, audited, audit_name); -+ } -+ -+ /* -+ * If here is no policy violations, or SE-PgSQL performs in permissive -+ * mode, or the client process peforms in permissive domain, it returns -+ * normally with 'true'. -+ */ -+ if (!denied || -+ !sepgsql_get_enforce() || -+ (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) != 0) -+ return true; -+ -+ /* -+ * Otherwise, it raises an error or returns 'false', depending on the -+ * caller's indication by 'abort'. -+ */ -+ if (abort) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("SELinux: security policy violation"))); -+ -+ return false; -+} -+ -+/* -+ * sepgsql_compute_create -+ * -+ * It returns a default security context to be assigned on a new database -+ * object. SELinux compute it based on a combination of client, upper object -+ * which owns the new object and object class. -+ * -+ * For example, when a client (staff_u:staff_r:staff_t:s0) tries to create -+ * a new table within a schema (system_u:object_r:sepgsql_schema_t:s0), -+ * SELinux looks-up its security policy. If it has a special rule on the -+ * combination of these security contexts and object class (db_table), -+ * it returns the security context suggested by the special rule. -+ * Otherwise, it returns the security context of schema, as is. -+ * -+ * We expect the caller already applies sanity/validation checks on the -+ * given security context. -+ * -+ * scontext : The security context of subject. In most cases, it is client. -+ * tcontext : The security context of the parent database object.. -+ * tclass : One of the object class code (SEPG_CLASS_*) declared in the -+ * header file. -+ */ -+char * -+sepgsql_compute_create(char *scontext, char *tcontext, uint16 tclass) -+{ -+ security_context_t ncontext; -+ security_class_t tclass_ex; -+ const char *tclass_name; -+ char *result; -+ -+ /* Get external code of the object class*/ -+ Assert(tclass < SEPG_CLASS_MAX); -+ -+ tclass_name = selinux_catalog[tclass].class_name; -+ tclass_ex = string_to_security_class(tclass_name); -+ -+ /* -+ * Ask SELinux what is the default context for the given object class -+ * on a pair of security contexts -+ */ -+ if (security_compute_create_raw(scontext, tcontext, -+ tclass_ex, &ncontext)) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("SELinux could not compute a new context: " -+ "scontext=%s tcontext=%s tclass=%s", -+ scontext, tcontext, tclass_name))); -+ -+ /* -+ * libselinux returns malloc()'ed string, so we need to copy it -+ * on the palloc()'ed region. -+ */ -+ PG_TRY(); -+ { -+ result = pstrdup(ncontext); -+ } -+ PG_CATCH(); -+ { -+ freecon(ncontext); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(ncontext); -+ -+ return result; -+} -diff --git a/src/backend/sepgsql/tablespace.c b/src/backend/sepgsql/tablespace.c -new file mode 100644 -index 0000000..1d3a35e ---- /dev/null -+++ b/src/backend/sepgsql/tablespace.c -@@ -0,0 +1,157 @@ -+/* -+ * tablespace.c -+ * -+ * SELinux hooks related to tablespaces -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_tablespace.h" -+#include "catalog/pg_seclabel.h" -+#include "commands/tablespace.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/syscache.h" -+#include "utils/lsyscache.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_tablespace_common(Oid tablespaceOid, uint32 required, bool abort) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t tsid; -+ bool retval; -+ const char *auname; -+ -+ tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(tablespaceOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for tablespace %u", tablespaceOid); -+ -+ tsid.relid = TableSpaceRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ auname = NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname); -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+ -+#endif -+ -+Oid -+sepgsql_tablespace_create(const char *tablespaceName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ -+ nsid = sepgsql_get_default_tuple_secid(TableSpaceRelationId); -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ tablespaceName, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_tablespace_alter(Oid tablespaceOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_tablespace_common(tablespaceOid, -+ SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_tablespace_relabel(Oid tablespaceOid, char *newLabel) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t tsid; -+ -+ tsid.relid = TableSpaceRelationId; -+ tsid.secid = seclabelTransInput(tsid.relid, newLabel); -+ -+ /* db_tuple:{update relabelfrom} */ -+ sepgsql_tablespace_common(tablespaceOid, -+ SEPG_DB_TUPLE__UPDATE | -+ SEPG_DB_TUPLE__RELABELFROM, true); -+ -+ /* db_procedure:{relabelto} */ -+ sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__RELABELTO, -+ get_tablespace_name(tablespaceOid), -+ true); -+ return tsid.secid; -+ } -+#endif -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL is not available"))); -+ return InvalidOid; -+} -+ -+void -+sepgsql_tablespace_drop(Oid tablespaceOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_tablespace_common(tablespaceOid, -+ SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_tablespace_grant(Oid tablespaceOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_tablespace_common(tablespaceOid, -+ SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_tablespace_getattr(Oid tablespaceOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_tablespace_common(tablespaceOid, -+ SEPG_DB_TUPLE__SELECT, true); -+ } -+#endif -+} -+ -+void -+sepgsql_tablespace_comment(Oid tablespaceOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_tablespace_common(tablespaceOid, -+ SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/tsearch.c b/src/backend/sepgsql/tsearch.c -new file mode 100644 -index 0000000..3d4180d ---- /dev/null -+++ b/src/backend/sepgsql/tsearch.c -@@ -0,0 +1,524 @@ -+/* -+ * tsearch.c -+ * -+ * SELinux hooks related to text searches -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_ts_config.h" -+#include "catalog/pg_ts_dict.h" -+#include "catalog/pg_ts_parser.h" -+#include "catalog/pg_ts_template.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/syscache.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_ts_config_common(Oid confOid, uint32 required, bool abort) -+{ -+ sepgsql_sid_t tsid; -+ HeapTuple tuple; -+ const char *auname; -+ bool retval; -+ -+ tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(confOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search configuration %u", confOid); -+ -+ tsid.relid = TSConfigRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ auname = NameStr(((Form_pg_ts_config) GETSTRUCT(tuple))->cfgname); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+ -+static Oid -+get_ts_config_namespace(Oid confOid) -+{ -+ HeapTuple tuple; -+ Oid namespaceId = InvalidOid; -+ -+ tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(confOid)); -+ if (HeapTupleIsValid(tuple)) -+ { -+ namespaceId = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace; -+ -+ ReleaseSysCache(tuple); -+ } -+ return namespaceId; -+} -+ -+bool -+sepgsql_ts_dict_common(Oid dictOid, uint32 required, bool abort) -+{ -+ sepgsql_sid_t tsid; -+ HeapTuple tuple; -+ const char *auname; -+ bool retval; -+ -+ tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); -+ -+ tsid.relid = TSDictionaryRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ auname = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+ -+static Oid -+get_ts_dict_namespace(Oid dictOid) -+{ -+ HeapTuple tuple; -+ Oid namespaceId = InvalidOid; -+ -+ tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictOid)); -+ if (HeapTupleIsValid(tuple)) -+ { -+ namespaceId = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace; -+ -+ ReleaseSysCache(tuple); -+ } -+ return namespaceId; -+} -+ -+bool -+sepgsql_ts_parser_common(Oid parseOid, uint32 required, bool abort) -+{ -+ sepgsql_sid_t tsid; -+ HeapTuple tuple; -+ const char *auname; -+ bool retval; -+ -+ tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(parseOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search parser %u", parseOid); -+ -+ tsid.relid = TSParserRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ auname = NameStr(((Form_pg_ts_parser) GETSTRUCT(tuple))->prsname); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+ -+static Oid -+get_ts_parser_namespace(Oid parseOid) -+{ -+ HeapTuple tuple; -+ Oid namespaceId = InvalidOid; -+ -+ tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(parseOid)); -+ if (HeapTupleIsValid(tuple)) -+ { -+ namespaceId = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace; -+ -+ ReleaseSysCache(tuple); -+ } -+ return namespaceId; -+} -+ -+bool -+sepgsql_ts_template_common(Oid templateOid, uint32 required, bool abort) -+{ -+ sepgsql_sid_t tsid; -+ HeapTuple tuple; -+ const char *auname; -+ bool retval; -+ -+ tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(templateOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search template %u", templateOid); -+ -+ tsid.relid = TSDictionaryRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ auname = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+ -+static Oid -+get_ts_template_namespace(Oid templateOid) -+{ -+ HeapTuple tuple; -+ Oid namespaceId = InvalidOid; -+ -+ tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(templateOid)); -+ if (HeapTupleIsValid(tuple)) -+ { -+ namespaceId = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace; -+ -+ ReleaseSysCache(tuple); -+ } -+ return namespaceId; -+} -+ -+#endif -+ -+Oid -+sepgsql_ts_config_create(const char *confName, Oid namespaceId) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid -+ = sepgsql_get_default_tuple_secid(TSConfigRelationId); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_tuple:{insert} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ confName, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_ts_config_alter(Oid confOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_ts_config_alter_rename(Oid confOid, const char *newName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_ts_config_namespace(confOid); -+ -+ /* db_schema:{add_name remove_name} */ -+ sepgsql_schema_common(namespaceId, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{update} */ -+ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_ts_config_drop(Oid confOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_ts_config_namespace(confOid); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{delete} */ -+ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_ts_config_comment(Oid confOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_ts_dict_create(const char *dictName, Oid namespaceId) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid -+ = sepgsql_get_default_tuple_secid(TSDictionaryRelationId); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_tuple:{insert} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ dictName, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_ts_dict_alter(Oid dictOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_ts_dict_alter_rename(Oid dictOid, const char *newName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_ts_dict_namespace(dictOid); -+ -+ /* db_schema:{add_name remove_name} */ -+ sepgsql_schema_common(namespaceId, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{update} */ -+ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_ts_dict_drop(Oid dictOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_ts_dict_namespace(dictOid); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{delete} */ -+ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_ts_dict_comment(Oid dictOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_ts_parser_create(const char *parseName, Oid namespaceId, -+ Oid startFunc, Oid tokenFunc, Oid endFunc, -+ Oid headlineFunc, Oid lextypeFunc) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid -+ = sepgsql_get_default_tuple_secid(TSParserRelationId); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(startFunc)) -+ sepgsql_proc_common(startFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(tokenFunc)) -+ sepgsql_proc_common(tokenFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(endFunc)) -+ sepgsql_proc_common(endFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(headlineFunc)) -+ sepgsql_proc_common(headlineFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(lextypeFunc)) -+ sepgsql_proc_common(lextypeFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ /* db_tuple:{insert} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ parseName, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_ts_parser_alter_rename(Oid parseOid, const char *newName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_ts_parser_namespace(parseOid); -+ -+ /* db_schema:{add_name remove_name} */ -+ sepgsql_schema_common(namespaceId, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{update} */ -+ sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_ts_parser_drop(Oid parseOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_ts_parser_namespace(parseOid); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{delete} */ -+ sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_ts_parser_comment(Oid parseOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_ts_template_create(const char *templateName, Oid namespaceId, -+ Oid initFunc, Oid lexizeFunc) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid -+ = sepgsql_get_default_tuple_secid(TSTemplateRelationId); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(initFunc)) -+ sepgsql_proc_common(initFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(lexizeFunc)) -+ sepgsql_proc_common(lexizeFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ /* db_tuple:{insert} */ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ templateName, -+ true); -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_ts_template_alter_rename(Oid templateOid, const char *newName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_ts_template_namespace(templateOid); -+ -+ /* db_schema:{add_name remove_name} */ -+ sepgsql_schema_common(namespaceId, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{update} */ -+ sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_ts_template_drop(Oid templateOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_ts_template_namespace(templateOid); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{delete} */ -+ sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_ts_template_comment(Oid templateOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -diff --git a/src/backend/sepgsql/type.c b/src/backend/sepgsql/type.c -new file mode 100644 -index 0000000..8cd65ae ---- /dev/null -+++ b/src/backend/sepgsql/type.c -@@ -0,0 +1,314 @@ -+/* -+ * type.c -+ * -+ * SELinux hooks related to types -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_cast.h" -+#include "catalog/pg_seclabel.h" -+#include "catalog/pg_type.h" -+#include "sepgsql/hooks.h" -+#include "sepgsql/sepgsql.h" -+#include "utils/builtins.h" -+#include "utils/lsyscache.h" -+#include "utils/syscache.h" -+ -+#ifdef HAVE_SELINUX -+bool -+sepgsql_type_common(Oid typeOid, uint32 required, bool abort) -+{ -+ sepgsql_sid_t tsid; -+ char *auname; -+ bool retval; -+ -+ tsid.relid = TypeRelationId; -+ tsid.secid = GetSysCacheSecid1(TYPEOID, -+ ObjectIdGetDatum(typeOid)); -+ -+ auname = format_type_be(typeOid); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ auname, -+ abort); -+ pfree(auname); -+ -+ return retval; -+} -+ -+bool -+sepgsql_cast_common(Oid srcTypeOid, Oid dstTypeOid, -+ uint32 required, bool abort) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t tsid; -+ bool retval; -+ -+ tuple = SearchSysCache2(CASTSOURCETARGET, -+ ObjectIdGetDatum(srcTypeOid), -+ ObjectIdGetDatum(dstTypeOid)); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for cast (%u,%u)", -+ srcTypeOid, dstTypeOid); -+ -+ tsid.relid = CastRelationId; -+ tsid.secid = HeapTupleGetSecid(tuple); -+ -+ retval = sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ NULL, -+ abort); -+ ReleaseSysCache(tuple); -+ -+ return retval; -+} -+ -+static Oid -+get_type_namespace(Oid typeOid) -+{ -+ HeapTuple tuple; -+ Oid namespaceId; -+ -+ tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid)); -+ if (!HeapTupleIsValid(tuple)) -+ return InvalidOid; -+ -+ namespaceId = ((Form_pg_type) GETSTRUCT(tuple))->typnamespace; -+ -+ ReleaseSysCache(tuple); -+ -+ return namespaceId; -+} -+#endif -+ -+Oid -+sepgsql_type_create(const char *typeName, Oid replaced, -+ Oid namespaceId, char typeType, -+ Oid inputFunc, Oid outputFunc, -+ Oid recvFunc, Oid sendFunc, -+ Oid modinFunc, Oid modoutFunc, Oid analyzeFunc) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ uint32 required; -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_tuple:{insert or update} */ -+ if (!OidIsValid(replaced)) -+ { -+ nsid = sepgsql_get_default_tuple_secid(TypeRelationId); -+ required = SEPG_DB_TUPLE__INSERT; -+ } -+ else -+ { -+ nsid.relid = TypeRelationId; -+ nsid.secid = GetSysCacheSecid1(TYPEOID, -+ ObjectIdGetDatum(replaced)); -+ required = SEPG_DB_TUPLE__UPDATE; -+ } -+ -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ required, -+ typeName, -+ true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(inputFunc)) -+ sepgsql_proc_common(inputFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(outputFunc)) -+ sepgsql_proc_common(outputFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(recvFunc)) -+ sepgsql_proc_common(recvFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(sendFunc)) -+ sepgsql_proc_common(sendFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(modinFunc)) -+ sepgsql_proc_common(modinFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(modoutFunc)) -+ sepgsql_proc_common(modoutFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(analyzeFunc)) -+ sepgsql_proc_common(analyzeFunc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_type_alter(Oid typeOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_type_alter_rename(Oid typeOid, const char *newName) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_type_namespace(typeOid); -+ -+ /* db_schema:{add_name remove_name} */ -+ sepgsql_schema_common(namespaceId, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_tuple:{update} */ -+ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_type_alter_schema(Oid typeOid, Oid newSchema) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ Oid namespaceId = get_type_namespace(typeOid); -+ -+ /* db_schema:{ remove_name } */ -+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ /* db_schema:{ add_name } */ -+ sepgsql_schema_common(newSchema, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_tuple:{update} */ -+ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_type_relabel(Oid typeOid, char *newLabel) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t tsid; -+ -+ tsid.relid = TypeRelationId; -+ tsid.secid = seclabelTransInput(tsid.relid, newLabel); -+ -+ /* db_tuple:{update relabelfrom} */ -+ sepgsql_type_common(typeOid, -+ SEPG_DB_TUPLE__UPDATE | -+ SEPG_DB_TUPLE__RELABELFROM, true); -+ -+ /* db_procedure:{relabelto} */ -+ sepgsql_client_perms(tsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__RELABELTO, -+ format_type_be(typeOid), -+ true); -+ return tsid.secid; -+ } -+#endif -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL is not available"))); -+ return InvalidOid; -+} -+ -+void -+sepgsql_type_drop(Oid typeOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{delete} */ -+ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_type_comment(Oid typeOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ /* db_tuple:{update} */ -+ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -+ -+Oid -+sepgsql_cast_create(Oid sourceTypeOid, Oid targetTypeOid, -+ char castMethod, Oid castFuncOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_sid_t nsid; -+ -+ /* db_tuple:{insert} */ -+ nsid = sepgsql_get_default_tuple_secid(CastRelationId); -+ sepgsql_client_perms(nsid, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ NULL, -+ true); -+ /* db_procedure:{install} */ -+ if (OidIsValid(castFuncOid)) -+ sepgsql_proc_common(castFuncOid, -+ SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return nsid.secid; -+ } -+#endif -+ return InvalidOid; -+} -+ -+void -+sepgsql_cast_drop(Oid sourceTypeOid, Oid targetTypeOid, bool cascade) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_cast_common(sourceTypeOid, targetTypeOid, -+ SEPG_DB_TUPLE__DELETE, true); -+ } -+#endif -+} -+ -+void -+sepgsql_cast_comment(Oid sourceTypeOid, Oid targetTypeOid) -+{ -+#ifdef HAVE_SELINUX -+ if (sepgsql_is_enabled()) -+ { -+ sepgsql_cast_common(sourceTypeOid, targetTypeOid, -+ SEPG_DB_TUPLE__UPDATE, true); -+ } -+#endif -+} -diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c -index 36da56d..6240a62 100644 ---- a/src/backend/storage/large_object/inv_api.c -+++ b/src/backend/storage/large_object/inv_api.c -@@ -197,14 +197,14 @@ getbytealen(bytea *data) - * in use. - */ - Oid --inv_create(Oid lobjId) -+inv_create(Oid lobjId, Oid securityId) - { - Oid lobjId_new; - - /* - * Create a new largeobject with empty data pages - */ -- lobjId_new = LargeObjectCreate(lobjId); -+ lobjId_new = LargeObjectCreate(lobjId, securityId); - - /* - * dependency on the owner of largeobject -diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c -index 5870081..7ae10b3 100644 ---- a/src/backend/tcop/fastpath.c -+++ b/src/backend/tcop/fastpath.c -@@ -26,6 +26,7 @@ - #include "libpq/pqformat.h" - #include "mb/pg_wchar.h" - #include "miscadmin.h" -+#include "sepgsql/hooks.h" - #include "tcop/fastpath.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" -@@ -348,6 +349,10 @@ HandleFunctionRequest(StringInfo msgBuf) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(fid)); - -+ /* SELinux checks */ -+ sepgsql_schema_search(fip->namespace, true); -+ sepgsql_proc_execute(fid); -+ - /* - * Restrict access to pg_get_expr(). This reflects the hack in - * transformFuncCall() in parse_expr.c, see comments there for an -diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c -index d60dc49..0ef21eb 100644 ---- a/src/backend/tcop/pquery.c -+++ b/src/backend/tcop/pquery.c -@@ -575,7 +575,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) - Assert(pstmt->hasReturning); - portal->tupDesc = - ExecCleanTypeFromTL(pstmt->planTree->targetlist, -- false); -+ false, false); - } - - /* -diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c -index ec36644..a25bd00 100644 ---- a/src/backend/tcop/utility.c -+++ b/src/backend/tcop/utility.c -@@ -165,6 +165,7 @@ check_xact_readonly(Node *parsetree) - case T_AlterRoleSetStmt: - case T_AlterObjectSchemaStmt: - case T_AlterOwnerStmt: -+ case T_AlterSecLabelStmt: - case T_AlterSeqStmt: - case T_AlterTableStmt: - case T_RenameStmt: -@@ -697,6 +698,10 @@ standard_ProcessUtility(Node *parsetree, - ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); - break; - -+ case T_AlterSecLabelStmt: -+ ExecAlterSecLabelStmt((AlterSecLabelStmt *) parsetree); -+ break; -+ - case T_AlterTableStmt: - { - List *stmts; -@@ -1761,6 +1766,46 @@ CreateCommandTag(Node *parsetree) - } - break; - -+ case T_AlterSecLabelStmt: -+ switch (((AlterSecLabelStmt *) parsetree)->objectType) -+ { -+ case OBJECT_DATABASE: -+ tag = "ALTER DATABASE"; -+ break; -+ case OBJECT_SCHEMA: -+ tag = "ALTER SCHEMA"; -+ break; -+ case OBJECT_TABLE: -+ case OBJECT_COLUMN: -+ tag = "ALTER TABLE"; -+ break; -+ case OBJECT_SEQUENCE: -+ tag = "ALTER SEQUENCE"; -+ break; -+ case OBJECT_VIEW: -+ tag = "ALTER VIEW"; -+ break; -+ case OBJECT_FUNCTION: -+ tag = "ALTER FUNCTION"; -+ break; -+ case OBJECT_AGGREGATE: -+ tag = "ALTER AGGREGATE"; -+ break; -+ case OBJECT_LARGEOBJECT: -+ tag = "ALTER LARGE OBJECT"; -+ break; -+ case OBJECT_TYPE: -+ tag = "ALTER TYPE"; -+ break; -+ case OBJECT_DOMAIN: -+ tag = "ALTER DOMAIN"; -+ break; -+ default: -+ tag = "???"; -+ break; -+ } -+ break; -+ - case T_AlterTableStmt: - switch (((AlterTableStmt *) parsetree)->relkind) - { -@@ -2353,6 +2398,10 @@ GetCommandLogLevel(Node *parsetree) - lev = LOGSTMT_DDL; - break; - -+ case T_AlterSecLabelStmt: -+ lev = LOGSTMT_DDL; -+ break; -+ - case T_AlterTableStmt: - lev = LOGSTMT_DDL; - break; -diff --git a/src/backend/tsearch/wparser.c b/src/backend/tsearch/wparser.c -index d74256f..f876951 100644 ---- a/src/backend/tsearch/wparser.c -+++ b/src/backend/tsearch/wparser.c -@@ -59,7 +59,7 @@ tt_setup_firstcall(FuncCallContext *funcctx, Oid prsid) - (Datum) 0)); - funcctx->user_fctx = (void *) st; - -- tupdesc = CreateTemplateTupleDesc(3, false); -+ tupdesc = CreateTemplateTupleDesc(3, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid", - INT4OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "alias", -@@ -205,7 +205,7 @@ prs_setup_firstcall(FuncCallContext *funcctx, Oid prsid, text *txt) - st->cur = 0; - - funcctx->user_fctx = (void *) st; -- tupdesc = CreateTemplateTupleDesc(2, false); -+ tupdesc = CreateTemplateTupleDesc(2, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid", - INT4OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "token", -diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c -index 79ca6c1..70fc9fb 100644 ---- a/src/backend/utils/adt/acl.c -+++ b/src/backend/utils/adt/acl.c -@@ -1695,7 +1695,7 @@ aclexplode(PG_FUNCTION_ARGS) - * build tupdesc for result tuples (matches out parameters in pg_proc - * entry) - */ -- tupdesc = CreateTemplateTupleDesc(4, false); -+ tupdesc = CreateTemplateTupleDesc(4, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "grantor", - OIDOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee", -diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c -index 705fcf1..e897f55 100644 ---- a/src/backend/utils/adt/datetime.c -+++ b/src/backend/utils/adt/datetime.c -@@ -4213,7 +4213,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS) - * build tupdesc for result tuples. This must match this function's - * pg_proc entry! - */ -- tupdesc = CreateTemplateTupleDesc(3, false); -+ tupdesc = CreateTemplateTupleDesc(3, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "abbrev", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "utc_offset", -@@ -4306,7 +4306,7 @@ pg_timezone_names(PG_FUNCTION_ARGS) - * build tupdesc for result tuples. This must match this function's - * pg_proc entry! - */ -- tupdesc = CreateTemplateTupleDesc(4, false); -+ tupdesc = CreateTemplateTupleDesc(4, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "abbrev", -diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c -index 8b5def4..9f79a5c 100644 ---- a/src/backend/utils/adt/dbsize.c -+++ b/src/backend/utils/adt/dbsize.c -@@ -21,6 +21,7 @@ - #include "commands/dbcommands.h" - #include "commands/tablespace.h" - #include "miscadmin.h" -+#include "sepgsql/hooks.h" - #include "storage/fd.h" - #include "utils/acl.h" - #include "utils/builtins.h" -@@ -90,6 +91,9 @@ calculate_database_size(Oid dbOid) - aclcheck_error(aclresult, ACL_KIND_DATABASE, - get_database_name(dbOid)); - -+ /* SELinux checks */ -+ sepgsql_database_getattr(dbOid); -+ - /* Shared storage in pg_global is not counted */ - - /* Include pg_default storage */ -@@ -178,6 +182,8 @@ calculate_tablespace_size(Oid tblspcOid) - aclcheck_error(aclresult, ACL_KIND_TABLESPACE, - get_tablespace_name(tblspcOid)); - } -+ /* SELinux checks */ -+ sepgsql_tablespace_getattr(tblspcOid); - - if (tblspcOid == DEFAULTTABLESPACE_OID) - snprintf(tblspcPath, MAXPGPATH, "base"); -diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c -index 863727d..d4a5220 100644 ---- a/src/backend/utils/adt/genfile.c -+++ b/src/backend/utils/adt/genfile.c -@@ -25,6 +25,7 @@ - #include "mb/pg_wchar.h" - #include "miscadmin.h" - #include "postmaster/syslogger.h" -+#include "sepgsql/hooks.h" - #include "storage/fd.h" - #include "utils/builtins.h" - #include "utils/memutils.h" -@@ -100,6 +101,8 @@ pg_read_file(PG_FUNCTION_ARGS) - - filename = convert_and_check_filename(filename_t); - -+ sepgsql_file_read(filename); -+ - if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL) - ereport(ERROR, - (errcode_for_file_access(), -@@ -164,6 +167,8 @@ pg_stat_file(PG_FUNCTION_ARGS) - - filename = convert_and_check_filename(filename_t); - -+ sepgsql_file_getattr(filename); -+ - if (stat(filename, &fst) < 0) - ereport(ERROR, - (errcode_for_file_access(), -@@ -173,7 +178,7 @@ pg_stat_file(PG_FUNCTION_ARGS) - * This record type had better match the output parameters declared for me - * in pg_proc.h. - */ -- tupdesc = CreateTemplateTupleDesc(6, false); -+ tupdesc = CreateTemplateTupleDesc(6, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, - "size", INT8OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, -@@ -236,6 +241,8 @@ pg_ls_dir(PG_FUNCTION_ARGS) - fctx = palloc(sizeof(directory_fctx)); - fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0)); - -+ sepgsql_dir_list(fctx->location); -+ - fctx->dirdesc = AllocateDir(fctx->location); - - if (!fctx->dirdesc) -diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c -index c72c4c1..0d23605 100644 ---- a/src/backend/utils/adt/lockfuncs.c -+++ b/src/backend/utils/adt/lockfuncs.c -@@ -85,7 +85,7 @@ pg_lock_status(PG_FUNCTION_ARGS) - - /* build tupdesc for result tuples */ - /* this had better match pg_locks view in system_views.sql */ -- tupdesc = CreateTemplateTupleDesc(14, false); -+ tupdesc = CreateTemplateTupleDesc(14, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database", -diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c -index 66c8598..c532dea 100644 ---- a/src/backend/utils/adt/misc.c -+++ b/src/backend/utils/adt/misc.c -@@ -322,7 +322,7 @@ pg_get_keywords(PG_FUNCTION_ARGS) - funcctx = SRF_FIRSTCALL_INIT(); - oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - -- tupdesc = CreateTemplateTupleDesc(3, false); -+ tupdesc = CreateTemplateTupleDesc(3, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catcode", -diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c -index 8b13c8a..09bb38e 100644 ---- a/src/backend/utils/adt/pgstatfuncs.c -+++ b/src/backend/utils/adt/pgstatfuncs.c -@@ -419,7 +419,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) - - oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - -- tupdesc = CreateTemplateTupleDesc(11, false); -+ tupdesc = CreateTemplateTupleDesc(11, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0); -diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c -index 2d0ab44..87eb953 100644 ---- a/src/backend/utils/adt/ri_triggers.c -+++ b/src/backend/utils/adt/ri_triggers.c -@@ -30,6 +30,7 @@ - - #include "postgres.h" - -+#include "access/sysattr.h" - #include "access/xact.h" - #include "catalog/pg_constraint.h" - #include "catalog/pg_operator.h" -@@ -39,6 +40,7 @@ - #include "parser/parse_coerce.h" - #include "parser/parse_relation.h" - #include "miscadmin.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -2624,6 +2626,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) - char fkrelname[MAX_QUOTED_REL_NAME_LEN]; - char pkattname[MAX_QUOTED_NAME_LEN + 3]; - char fkattname[MAX_QUOTED_NAME_LEN + 3]; -+ Bitmapset *pkColumns = NULL; -+ Bitmapset *fkColumns = NULL; - const char *sep; - int i; - int old_work_mem; -@@ -2645,6 +2649,18 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) - - ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false); - -+ for (i = 0; i < riinfo.nkeys; i++) -+ { -+ fkColumns = bms_add_member(fkColumns, riinfo.fk_attnums[i] -+ - FirstLowInvalidHeapAttributeNumber); -+ pkColumns = bms_add_member(pkColumns, riinfo.pk_attnums[i] -+ - FirstLowInvalidHeapAttributeNumber); -+ } -+ -+ if (!sepgsql_relation_perms(RelationGetRelid(pk_rel), -+ ACL_SELECT, pkColumns, NULL, false)) -+ return false; -+ - /*---------- - * The query string built is: - * SELECT fk.keycols FROM ONLY relname fk -@@ -3259,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]; - -@@ -3342,6 +3359,10 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan, - SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner, - save_sec_context | SECURITY_LOCAL_USERID_CHANGE); - -+ /* Switch rowlv control mode */ -+ temp_rowlv = (detectNewRows ? SEPGSQL_ROWLV_ABORT : SEPGSQL_ROWLV_FILTER); -+ save_rowlv = sepgsql_rowlv_set_mode(temp_rowlv); -+ - /* Finally we can run the query. */ - spi_result = SPI_execute_snapshot(qplan, - vals, nulls, -@@ -3351,6 +3372,9 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan, - /* Restore UID and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); - -+ /* Restore Rowlv control mode */ -+ sepgsql_rowlv_set_mode(save_rowlv); -+ - /* Check result */ - if (spi_result < 0) - elog(ERROR, "SPI_execute_snapshot returned %d", spi_result); -diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c -index 8c18a26..843d702 100644 ---- a/src/backend/utils/adt/tid.c -+++ b/src/backend/utils/adt/tid.c -@@ -27,6 +27,7 @@ - #include "libpq/pqformat.h" - #include "miscadmin.h" - #include "parser/parsetree.h" -+#include "sepgsql/hooks.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/rel.h" -@@ -347,6 +348,8 @@ currtid_byreloid(PG_FUNCTION_ARGS) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(rel)); -+ /* SELinux checks */ -+ sepgsql_relation_getattr(RelationGetRelid(rel)); - - if (rel->rd_rel->relkind == RELKIND_VIEW) - return currtid_for_view(rel, tid); -@@ -377,6 +380,8 @@ currtid_byrelname(PG_FUNCTION_ARGS) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(rel)); -+ /* SELinux checks */ -+ sepgsql_relation_getattr(RelationGetRelid(rel)); - - if (rel->rd_rel->relkind == RELKIND_VIEW) - return currtid_for_view(rel, tid); -diff --git a/src/backend/utils/adt/trigfuncs.c b/src/backend/utils/adt/trigfuncs.c -index d831b60..badc1f5 100644 ---- a/src/backend/utils/adt/trigfuncs.c -+++ b/src/backend/utils/adt/trigfuncs.c -@@ -76,6 +76,10 @@ suppress_redundant_updates_trigger(PG_FUNCTION_ARGS) - !OidIsValid(HeapTupleHeaderGetOid(newheader))) - HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader)); - -+ if (trigdata->tg_relation->rd_rel->relhassecids && -+ !OidIsValid(HeapTupleHeaderGetSecid(newheader))) -+ HeapTupleHeaderSetSecid(newheader, HeapTupleHeaderGetSecid(oldheader)); -+ - /* if the tuple payload is the same ... */ - if (newtuple->t_len == oldtuple->t_len && - newheader->t_hoff == oldheader->t_hoff && -diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c -index 94c242d..b44cb63 100644 ---- a/src/backend/utils/adt/tsvector_op.c -+++ b/src/backend/utils/adt/tsvector_op.c -@@ -975,7 +975,7 @@ ts_setup_firstcall(FunctionCallInfo fcinfo, FuncCallContext *funcctx, - } - Assert(stat->stackpos <= stat->maxdepth); - -- tupdesc = CreateTemplateTupleDesc(3, false); -+ tupdesc = CreateTemplateTupleDesc(3, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "ndoc", -diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c -index 06e2549..56c5df9 100644 ---- a/src/backend/utils/cache/plancache.c -+++ b/src/backend/utils/cache/plancache.c -@@ -926,12 +926,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; -@@ -942,13 +942,13 @@ PlanCacheComputeResultDesc(List *stmt_list) - { - query = (Query *) node; - Assert(query->returningList); -- return ExecCleanTypeFromTL(query->returningList, false); -+ return ExecCleanTypeFromTL(query->returningList, false, false); - } - if (IsA(node, PlannedStmt)) - { - pstmt = (PlannedStmt *) node; - Assert(pstmt->hasReturning); -- return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); -+ return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false); - } - /* other cases shouldn't happen, but return NULL */ - break; -diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c -index 510f077..430d89e 100644 ---- a/src/backend/utils/cache/relcache.c -+++ b/src/backend/utils/cache/relcache.c -@@ -215,7 +215,7 @@ static void write_relcache_init_file(bool shared); - static void write_item(const void *data, Size len, FILE *fp); - - static void formrdesc(const char *relationName, Oid relationReltype, -- bool isshared, bool hasoids, -+ bool isshared, bool hasoids, bool hassecids, - int natts, const FormData_pg_attribute *attrs); - - static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK); -@@ -354,7 +354,8 @@ AllocateRelationDesc(Form_pg_class relp) - - /* and allocate attribute tuple form storage */ - relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, -- relationForm->relhasoids); -+ relationForm->relhasoids, -+ relationForm->relhassecids); - /* which we mark as a reference-counted tupdesc */ - relation->rd_att->tdrefcount = 1; - -@@ -436,6 +437,7 @@ RelationBuildTupleDesc(Relation relation) - relation->rd_att->tdtypeid = relation->rd_rel->reltype; - relation->rd_att->tdtypmod = -1; /* unnecessary, but... */ - relation->rd_att->tdhasoid = relation->rd_rel->relhasoids; -+ relation->rd_att->tdhassecid = relation->rd_rel->relhassecids; - - constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext, - sizeof(TupleConstr)); -@@ -1395,7 +1397,7 @@ LookupOpclassInfo(Oid operatorClassOid, - */ - static void - formrdesc(const char *relationName, Oid relationReltype, -- bool isshared, bool hasoids, -+ bool isshared, bool hasoids, bool hassecids, - int natts, const FormData_pg_attribute *attrs) - { - Relation relation; -@@ -1458,6 +1460,7 @@ formrdesc(const char *relationName, Oid relationReltype, - relation->rd_rel->reltuples = 1; - relation->rd_rel->relkind = RELKIND_RELATION; - relation->rd_rel->relhasoids = hasoids; -+ relation->rd_rel->relhassecids = hassecids; - relation->rd_rel->relnatts = (int16) natts; - - /* -@@ -1467,7 +1470,7 @@ formrdesc(const char *relationName, Oid relationReltype, - * because it will never be replaced. The data comes from - * src/include/catalog/ headers via genbki.pl. - */ -- relation->rd_att = CreateTemplateTupleDesc(natts, hasoids); -+ relation->rd_att = CreateTemplateTupleDesc(natts, hasoids, hassecids); - relation->rd_att->tdrefcount = 1; /* mark as refcounted */ - - relation->rd_att->tdtypeid = relationReltype; -@@ -2542,6 +2545,7 @@ RelationBuildLocalRelation(const char *relname, - - rel->rd_rel->relkind = RELKIND_UNCATALOGED; - rel->rd_rel->relhasoids = rel->rd_att->tdhasoid; -+ rel->rd_rel->relhassecids = rel->rd_att->tdhassecid; - rel->rd_rel->relnatts = natts; - rel->rd_rel->reltype = InvalidOid; - /* needed when bootstrapping: */ -@@ -2784,11 +2788,11 @@ RelationCacheInitializePhase2(void) - if (!load_relcache_init_file(true)) - { - formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true, -- true, Natts_pg_database, Desc_pg_database); -+ true, true, Natts_pg_database, Desc_pg_database); - formrdesc("pg_authid", AuthIdRelation_Rowtype_Id, true, -- true, Natts_pg_authid, Desc_pg_authid); -+ true, true, Natts_pg_authid, Desc_pg_authid); - formrdesc("pg_auth_members", AuthMemRelation_Rowtype_Id, true, -- false, Natts_pg_auth_members, Desc_pg_auth_members); -+ false, false, Natts_pg_auth_members, Desc_pg_auth_members); - - #define NUM_CRITICAL_SHARED_RELS 3 /* fix if you change list above */ - } -@@ -2839,13 +2843,13 @@ RelationCacheInitializePhase3(void) - needNewCacheFile = true; - - formrdesc("pg_class", RelationRelation_Rowtype_Id, false, -- true, Natts_pg_class, Desc_pg_class); -+ true, true, Natts_pg_class, Desc_pg_class); - formrdesc("pg_attribute", AttributeRelation_Rowtype_Id, false, -- false, Natts_pg_attribute, Desc_pg_attribute); -+ false, true, Natts_pg_attribute, Desc_pg_attribute); - formrdesc("pg_proc", ProcedureRelation_Rowtype_Id, false, -- true, Natts_pg_proc, Desc_pg_proc); -+ true, true, Natts_pg_proc, Desc_pg_proc); - formrdesc("pg_type", TypeRelation_Rowtype_Id, false, -- true, Natts_pg_type, Desc_pg_type); -+ true, true, Natts_pg_type, Desc_pg_type); - - #define NUM_CRITICAL_LOCAL_RELS 4 /* fix if you change list above */ - } -@@ -2998,6 +3002,7 @@ RelationCacheInitializePhase3(void) - Assert(relation->rd_att->tdtypeid == relp->reltype); - Assert(relation->rd_att->tdtypmod == -1); - Assert(relation->rd_att->tdhasoid == relp->relhasoids); -+ Assert(relation->rd_att->tdhassecid == relp->relhassecids); - - ReleaseSysCache(htup); - -@@ -3108,7 +3113,7 @@ load_critical_index(Oid indexoid, Oid heapoid) - */ - static TupleDesc - BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, -- bool hasoids) -+ bool hasoids, bool hassecids) - { - TupleDesc result; - MemoryContext oldcxt; -@@ -3116,7 +3121,7 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, - - oldcxt = MemoryContextSwitchTo(CacheMemoryContext); - -- result = CreateTemplateTupleDesc(natts, hasoids); -+ result = CreateTemplateTupleDesc(natts, hasoids, hassecids); - result->tdtypeid = RECORDOID; /* not right, but we don't care */ - result->tdtypmod = -1; - -@@ -3146,7 +3151,7 @@ GetPgClassDescriptor(void) - if (pgclassdesc == NULL) - pgclassdesc = BuildHardcodedDescriptor(Natts_pg_class, - Desc_pg_class, -- true); -+ true, true); - - return pgclassdesc; - } -@@ -3160,7 +3165,7 @@ GetPgIndexDescriptor(void) - if (pgindexdesc == NULL) - pgindexdesc = BuildHardcodedDescriptor(Natts_pg_index, - Desc_pg_index, -- false); -+ false, false); - - return pgindexdesc; - } -@@ -3976,7 +3981,8 @@ load_relcache_init_file(bool shared) - - /* initialize attribute tuple forms */ - rel->rd_att = CreateTemplateTupleDesc(relform->relnatts, -- relform->relhasoids); -+ relform->relhasoids, -+ relform->relhassecids); - rel->rd_att->tdrefcount = 1; /* mark as refcounted */ - - rel->rd_att->tdtypeid = relform->reltype; -diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c -index 0224cfb..3137199 100644 ---- a/src/backend/utils/cache/syscache.c -+++ b/src/backend/utils/cache/syscache.c -@@ -895,6 +895,30 @@ GetSysCacheOid(int cacheId, - return result; - } - -+/* -+ * GetSysCacheSecid -+ * -+ * A convenience routine that does SearchSysCache and returns the -+ * security-id of the found tuple, or InvalidOid if no tuple could -+ * be found. No lock is retained on the syscache entry. -+ */ -+Oid -+GetSysCacheSecid(int cacheId, -+ Datum key1, -+ Datum key2, -+ Datum key3, -+ Datum key4) -+{ -+ HeapTuple tuple; -+ Oid result; -+ -+ tuple = SearchSysCache(cacheId, key1, key2, key3, key4); -+ if (!HeapTupleIsValid(tuple)) -+ return InvalidOid; -+ result = HeapTupleGetSecid(tuple); -+ ReleaseSysCache(tuple); -+ return result; -+} - - /* - * SearchSysCacheAttName -diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c -index 04f91f1..6f82199 100644 ---- a/src/backend/utils/fmgr/fmgr.c -+++ b/src/backend/utils/fmgr/fmgr.c -@@ -24,6 +24,7 @@ - #include "miscadmin.h" - #include "nodes/nodeFuncs.h" - #include "pgstat.h" -+#include "sepgsql/hooks.h" - #include "utils/builtins.h" - #include "utils/fmgrtab.h" - #include "utils/guc.h" -@@ -190,6 +191,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, - finfo->fn_extra = NULL; - finfo->fn_mcxt = mcxt; - finfo->fn_expr = NULL; /* caller may set this later */ -+ finfo->fn_seclabel = NULL; - - if ((fbp = fmgr_isbuiltin(functionId)) != NULL) - { -@@ -228,15 +230,22 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, - * ability to set the track_functions GUC as a local GUC parameter of an - * interesting function and have the right things happen. - */ -- if (!ignore_security && -- (procedureStruct->prosecdef || -- !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig))) -+ if (!ignore_security) - { -- finfo->fn_addr = fmgr_security_definer; -- finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ -- finfo->fn_oid = functionId; -- ReleaseSysCache(procedureTuple); -- return; -+ char *seclabel -+ = sepgsql_proc_domtrans(procedureTuple, mcxt); -+ -+ if (procedureStruct->prosecdef || -+ !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig) || -+ seclabel != NULL) -+ { -+ finfo->fn_addr = fmgr_security_definer; -+ finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ -+ finfo->fn_oid = functionId; -+ finfo->fn_seclabel = seclabel; -+ ReleaseSysCache(procedureTuple); -+ return; -+ } - } - - switch (procedureStruct->prolang) -@@ -877,6 +886,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS) - FmgrInfo *save_flinfo; - Oid save_userid; - int save_sec_context; -+ char *save_seclabel; - volatile int save_nestlevel; - PgStat_FunctionCallUsage fcusage; - -@@ -939,6 +949,10 @@ fmgr_security_definer(PG_FUNCTION_ARGS) - PGC_S_SESSION, - GUC_ACTION_SAVE); - } -+ if (fcinfo->flinfo->fn_seclabel) -+ save_seclabel = sepgsql_set_client_label(fcinfo->flinfo->fn_seclabel); -+ else -+ save_seclabel = NULL; - - /* - * We don't need to restore GUC or userid settings on error, because the -@@ -978,6 +992,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS) - AtEOXact_GUC(true, save_nestlevel); - if (OidIsValid(fcache->userid)) - SetUserIdAndSecContext(save_userid, save_sec_context); -+ if (fcinfo->flinfo->fn_seclabel) -+ sepgsql_set_client_label(save_seclabel); - - return result; - } -diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c -index d946aab..f7581a7 100644 ---- a/src/backend/utils/fmgr/funcapi.c -+++ b/src/backend/utils/fmgr/funcapi.c -@@ -1105,7 +1105,7 @@ build_function_result_tupdesc_d(Datum proallargtypes, - if (numoutargs < 2) - return NULL; - -- desc = CreateTemplateTupleDesc(numoutargs, false); -+ desc = CreateTemplateTupleDesc(numoutargs, false, false); - for (i = 0; i < numoutargs; i++) - { - TupleDescInitEntry(desc, i + 1, -@@ -1220,7 +1220,7 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases) - /* OK, get the column alias */ - attname = strVal(linitial(colaliases)); - -- tupdesc = CreateTemplateTupleDesc(1, false); -+ tupdesc = CreateTemplateTupleDesc(1, false, false); - TupleDescInitEntry(tupdesc, - (AttrNumber) 1, - attname, -diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c -index fc97683..1fcb33f 100644 ---- a/src/backend/utils/init/postinit.c -+++ b/src/backend/utils/init/postinit.c -@@ -37,6 +37,7 @@ - #include "postmaster/autovacuum.h" - #include "postmaster/postmaster.h" - #include "replication/walsender.h" -+#include "sepgsql/hooks.h" - #include "storage/bufmgr.h" - #include "storage/fd.h" - #include "storage/ipc.h" -@@ -306,6 +307,9 @@ CheckMyDatabase(const char *name, bool am_superuser) - errmsg("permission denied for database \"%s\"", name), - errdetail("User does not have CONNECT privilege."))); - -+ /* SELinux checks */ -+ sepgsql_database_connect(MyDatabaseId); -+ - /* - * Check connection limit for this database. - * -@@ -817,6 +821,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, - /* set up ACL framework (so CheckMyDatabase can check permissions) */ - initialize_acl(); - -+ /* set up enhanced security feature */ -+ sepgsql_initialize(); -+ - /* - * Re-read the 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 4e55b16..a9f9f7e 100644 ---- a/src/backend/utils/misc/guc.c -+++ b/src/backend/utils/misc/guc.c -@@ -31,6 +31,7 @@ - #include "access/twophase.h" - #include "access/xact.h" - #include "catalog/namespace.h" -+#include "catalog/pg_seclabel.h" - #include "commands/async.h" - #include "commands/prepare.h" - #include "commands/vacuum.h" -@@ -56,6 +57,7 @@ - #include "postmaster/syslogger.h" - #include "postmaster/walwriter.h" - #include "replication/walsender.h" -+#include "sepgsql/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/standby.h" - #include "storage/fd.h" -@@ -335,6 +337,18 @@ static const struct config_enum_entry constraint_exclusion_options[] = { - {NULL, 0, false} - }; - -+#ifdef HAVE_SELINUX -+static const struct config_enum_entry sepostgresql_options[] = { -+ {"default", SEPGSQL_MODE_DEFAULT, false}, -+ {"enforcing", SEPGSQL_MODE_ENFORCING, false}, -+ {"permissive", SEPGSQL_MODE_PERMISSIVE, false}, -+ {"disabled", SEPGSQL_MODE_DISABLED, false}, -+ {"on", SEPGSQL_MODE_DEFAULT, true}, -+ {"off", SEPGSQL_MODE_DISABLED, true}, -+ {NULL, 0, false}, -+}; -+#endif -+ - /* - * Options for enum values stored in other modules - */ -@@ -364,6 +378,7 @@ bool log_btree_build_stats = false; - - bool check_function_bodies = true; - bool default_with_oids = false; -+bool default_with_secids = true; - bool SQL_inheritance = true; - - bool Password_encryption = true; -@@ -1103,6 +1118,14 @@ static struct config_bool ConfigureNamesBool[] = - false, NULL, NULL - }, - { -+ {"default_with_secids", PGC_USERSET, CONN_AUTH_SETTINGS, -+ gettext_noop("Create new tables with security-ids by default."), -+ NULL -+ }, -+ &default_with_secids, -+ true, NULL, NULL -+ }, -+ { - {"logging_collector", PGC_POSTMASTER, LOGGING_WHERE, - gettext_noop("Start a subprocess to capture stderr output and/or csvlogs into log files."), - NULL -@@ -1270,6 +1293,36 @@ static struct config_bool ConfigureNamesBool[] = - false, NULL, NULL - }, - -+ { -+ {"ignore_security_label_input", PGC_USERSET, CONN_AUTH_SECURITY, -+ gettext_noop("Disables to assign used provided security label"), -+ NULL, -+ GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE -+ }, -+ &ignore_security_label_input, -+ false, NULL, NULL -+ }, -+#ifdef HAVE_SELINUX -+ { -+ {"sepostgresql_mcstrans", PGC_USERSET, CONN_AUTH_SECURITY, -+ gettext_noop("Enables to show security context in human-readable form"), -+ NULL, -+ GUC_NOT_IN_SAMPLE -+ }, -+ &sepgsql_mcstrans, -+ true, NULL, NULL -+ }, -+ { -+ {"sepostgresql_debug_audit", PGC_USERSET, CONN_AUTH_SECURITY, -+ gettext_noop("Enables to show audit logs for debugging"), -+ NULL, -+ GUC_NOT_IN_SAMPLE -+ }, -+ &sepgsql_debug_audit, -+ false, NULL, NULL -+ }, -+#endif -+ - /* End-of-list marker */ - { - {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL -@@ -2844,7 +2897,16 @@ static struct config_enum ConfigureNamesEnum[] = - &xmloption, - XMLOPTION_CONTENT, xmloption_options, NULL, NULL - }, -- -+#ifdef HAVE_SELINUX -+ { -+ {"sepostgresql", PGC_POSTMASTER, CONN_AUTH_SECURITY, -+ gettext_noop("Enables to set SE-PostgreSQL's mode"), -+ NULL -+ }, -+ &sepostgresql_mode, -+ SEPGSQL_MODE_DEFAULT, sepostgresql_options, NULL, sepgsql_show_mode -+ }, -+#endif - - /* End-of-list marker */ - { -@@ -6139,7 +6201,7 @@ GetPGVariableResultDesc(const char *name) - if (guc_name_compare(name, "all") == 0) - { - /* need a tuple descriptor representing three TEXT columns */ -- tupdesc = CreateTemplateTupleDesc(3, false); -+ tupdesc = CreateTemplateTupleDesc(3, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", -@@ -6155,7 +6217,7 @@ GetPGVariableResultDesc(const char *name) - (void) GetConfigOptionByName(name, &varname); - - /* need a tuple descriptor representing a single TEXT column */ -- tupdesc = CreateTemplateTupleDesc(1, false); -+ tupdesc = CreateTemplateTupleDesc(1, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, - TEXTOID, -1, 0); - } -@@ -6178,7 +6240,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest) - value = GetConfigOptionByName(name, &varname); - - /* need a tuple descriptor representing a single TEXT column */ -- tupdesc = CreateTemplateTupleDesc(1, false); -+ tupdesc = CreateTemplateTupleDesc(1, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, - TEXTOID, -1, 0); - -@@ -6205,7 +6267,7 @@ ShowAllGUCConfig(DestReceiver *dest) - bool isnull[3] = {false, false, false}; - - /* need a tuple descriptor representing three TEXT columns */ -- tupdesc = CreateTemplateTupleDesc(3, false); -+ tupdesc = CreateTemplateTupleDesc(3, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", -@@ -6600,7 +6662,7 @@ show_all_settings(PG_FUNCTION_ARGS) - * need a tuple descriptor representing NUM_PG_SETTINGS_ATTS columns - * of the appropriate types - */ -- tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false); -+ tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", -diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample -index a3880da..7537ac8 100644 ---- a/src/backend/utils/misc/postgresql.conf.sample -+++ b/src/backend/utils/misc/postgresql.conf.sample -@@ -76,6 +76,8 @@ - - # - Security and Authentication - - -+#sepostgresql = disabled # default|enforcing|permissive|disabled -+ - #authentication_timeout = 1min # 1s-600s - #ssl = off # (change requires restart) - #ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH' # allowed SSL ciphers -diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c -index 19c594d..b80b6ac 100644 ---- a/src/backend/utils/mmgr/portalmem.c -+++ b/src/backend/utils/mmgr/portalmem.c -@@ -949,7 +949,7 @@ pg_cursor(PG_FUNCTION_ARGS) - * build tupdesc for result tuples. This must match the definition of the - * pg_cursors view in system_views.sql - */ -- tupdesc = CreateTemplateTupleDesc(6, false); -+ tupdesc = CreateTemplateTupleDesc(6, false, false); - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", -diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c -index 0aee70d..ceb9a87 100644 ---- a/src/bin/initdb/initdb.c -+++ b/src/bin/initdb/initdb.c -@@ -87,6 +87,7 @@ static bool debug = false; - static bool noclean = false; - static bool show_setting = false; - static char *xlog_dir = ""; -+static bool enable_selinux = false; - - - /* internal vars */ -@@ -1163,6 +1164,13 @@ setup_config(void) - "#default_text_search_config = 'pg_catalog.simple'", - repltok); - -+ if (enable_selinux) -+ { -+ strcpy(repltok, "sepostgresql = default"); -+ conflines = replace_token(conflines, -+ "#sepostgresql = disabled", repltok); -+ } -+ - snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data); - - writefile(path, conflines); -@@ -2394,6 +2402,7 @@ usage(const char *progname) - printf(_(" -U, --username=NAME database superuser name\n")); - printf(_(" -W, --pwprompt prompt for a password for the new superuser\n")); - printf(_(" -X, --xlogdir=XLOGDIR location for the transaction log directory\n")); -+ printf(_(" --enable-selinux enables SELinux support\n")); - printf(_("\nLess commonly used options:\n")); - printf(_(" -d, --debug generate lots of debugging output\n")); - printf(_(" -L DIRECTORY where to find the input files\n")); -@@ -2436,6 +2445,7 @@ main(int argc, char *argv[]) - {"show", no_argument, NULL, 's'}, - {"noclean", no_argument, NULL, 'n'}, - {"xlogdir", required_argument, NULL, 'X'}, -+ {"enable-selinux", no_argument, NULL, 10}, - {NULL, 0, NULL, 0} - }; - -@@ -2545,6 +2555,9 @@ main(int argc, char *argv[]) - case 9: - pwfilename = xstrdup(optarg); - break; -+ case 10: -+ enable_selinux = true; -+ break; - case 's': - show_setting = true; - break; -diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h -index 5a73779..67fce14 100644 ---- a/src/bin/pg_dump/pg_backup.h -+++ b/src/bin/pg_dump/pg_backup.h -@@ -103,6 +103,7 @@ typedef struct _restoreOptions - * restore */ - int use_setsessauth;/* Use SET SESSION AUTHORIZATION commands - * instead of OWNER TO */ -+ int noSecLabel; /* Don't try to restore security labels */ - char *superuser; /* Username to use as superuser */ - char *use_role; /* Issue SET ROLE to this */ - int dataOnly; -@@ -167,7 +168,7 @@ extern void ArchiveEntry(Archive *AHX, - CatalogId catalogId, DumpId dumpId, - const char *tag, - const char *namespace, const char *tablespace, -- const char *owner, bool withOids, -+ const char *owner, bool withOids, bool withSecids, - const char *desc, teSection section, - const char *defn, - const char *dropStmt, const char *copyStmt, -diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c -index 6528f4d..e482bde 100644 ---- a/src/bin/pg_dump/pg_backup_archiver.c -+++ b/src/bin/pg_dump/pg_backup_archiver.c -@@ -542,6 +542,8 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, - } - else - { -+ bool need_reset = false; -+ - _disableTriggersIfNecessary(AH, te, ropt); - - /* Select owner and schema as necessary */ -@@ -577,6 +579,16 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, - "ONLY " : ""), - fmtId(te->tag)); - } -+ /* -+ * If data section has security_label, but pg_restore works -+ * with --no-security-label, we set server flag to ignore -+ * the security label input. -+ */ -+ if (ropt->noSecLabel && te->withSecids) -+ { -+ ahprintf(AH, "SET ignore_security_label_input = on;\n\n"); -+ need_reset = true; -+ } - - /* - * If we have a copy statement, use it. As of V1.3, these -@@ -597,6 +609,9 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, - - AH->writingCopyData = false; - -+ if (need_reset) -+ ahprintf(AH, "RESET ignore_security_label_input;\n\n"); -+ - /* close out the transaction started above */ - if (is_parallel && te->created) - CommitTransaction(AH); -@@ -714,7 +729,7 @@ ArchiveEntry(Archive *AHX, - const char *tag, - const char *namespace, - const char *tablespace, -- const char *owner, bool withOids, -+ const char *owner, bool withOids, bool withSecids, - const char *desc, teSection section, - const char *defn, - const char *dropStmt, const char *copyStmt, -@@ -746,6 +761,7 @@ ArchiveEntry(Archive *AHX, - newToc->tablespace = tablespace ? strdup(tablespace) : NULL; - newToc->owner = strdup(owner); - newToc->withOids = withOids; -+ newToc->withSecids = withSecids; - newToc->desc = strdup(desc); - newToc->defn = strdup(defn); - newToc->dropStmt = strdup(dropStmt); -@@ -2048,6 +2064,7 @@ WriteToc(ArchiveHandle *AH) - WriteStr(AH, te->tablespace); - WriteStr(AH, te->owner); - WriteStr(AH, te->withOids ? "true" : "false"); -+ WriteStr(AH, te->withSecids ? "true" : "false"); - - /* Dump list of dependencies */ - for (i = 0; i < te->nDeps; i++) -@@ -2159,6 +2176,16 @@ ReadToc(ArchiveHandle *AH) - else - te->withOids = true; - -+ if (AH->version >= K_VERS_1_13) -+ { -+ if (strcmp(ReadStr(AH), "true") == 0) -+ te->withSecids = true; -+ else -+ te->withSecids = false; -+ } -+ else -+ te->withSecids = false; -+ - /* Read TOC entry dependencies */ - if (AH->version >= K_VERS_1_5) - { -@@ -2277,6 +2304,9 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) - if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te)) - return 0; - -+ if (ropt->noSecLabel && strcmp(te->desc, "LABEL") == 0) -+ return 0; -+ - /* Ignore DATABASE entry unless we should create it */ - if (!ropt->createDB && strcmp(te->desc, "DATABASE") == 0) - return 0; -@@ -2343,6 +2373,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) - (strcmp(te->desc, "ACL") == 0 && - strncmp(te->tag, "LARGE OBJECT ", 13) == 0) || - (strcmp(te->desc, "COMMENT") == 0 && -+ strncmp(te->tag, "LARGE OBJECT ", 13) == 0) || -+ (strcmp(te->desc, "LABEL") == 0 && - strncmp(te->tag, "LARGE OBJECT ", 13) == 0)) - res = res & REQ_DATA; - else -@@ -2494,6 +2526,36 @@ _doSetWithOids(ArchiveHandle *AH, const bool withOids) - destroyPQExpBuffer(cmd); - } - -+/* -+ * Issue a SET default_with_secids command. Caller is responsible -+ * for updating state if appropriate. -+ */ -+static void -+_doSetWithSecids(ArchiveHandle *AH, const bool withSecids) -+{ -+ PQExpBuffer cmd = createPQExpBuffer(); -+ -+ appendPQExpBuffer(cmd, "SET default_with_secids = %s;", withSecids ? -+ "true" : "false"); -+ -+ if (RestoringToDB(AH)) -+ { -+ PGresult *res; -+ -+ res = PQexec(AH->connection, cmd->data); -+ -+ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) -+ warn_or_die_horribly(AH, modulename, -+ "could not set default_with_secids: %s", -+ PQerrorMessage(AH->connection)); -+ -+ PQclear(res); -+ } -+ else -+ ahprintf(AH, "%s\n\n", cmd->data); -+ -+ destroyPQExpBuffer(cmd); -+} - - /* - * Issue the commands to connect to the specified database. -@@ -2592,6 +2654,18 @@ _setWithOids(ArchiveHandle *AH, TocEntry *te) - } - } - -+/* -+ * Set the proper default_with_secids value for the table. -+ */ -+static void -+_setWithSecids(ArchiveHandle *AH, TocEntry *te) -+{ -+ if (AH->currWithSecids != te->withSecids) -+ { -+ _doSetWithSecids(AH, te->withSecids); -+ AH->currWithSecids = te->withSecids; -+ } -+} - - /* - * Issue the commands to select the specified schema as the current schema -@@ -2829,9 +2903,12 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat - _selectOutputSchema(AH, te->namespace); - _selectTablespace(AH, te->tablespace); - -- /* Set up OID mode too */ -+ /* Set up OID/SECID mode too */ - if (strcmp(te->desc, "TABLE") == 0) -+ { - _setWithOids(AH, te); -+ _setWithSecids(AH, te); -+ } - - /* Emit header comment for item */ - if (!AH->noTocComments) -diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h -index 9f826b6..a46be6b 100644 ---- a/src/bin/pg_dump/pg_backup_archiver.h -+++ b/src/bin/pg_dump/pg_backup_archiver.h -@@ -63,7 +63,7 @@ typedef z_stream *z_streamp; - - /* Current archive version number (the format we can output) */ - #define K_VERS_MAJOR 1 --#define K_VERS_MINOR 12 -+#define K_VERS_MINOR 13 - #define K_VERS_REV 0 - - /* Data block types */ -@@ -89,9 +89,10 @@ typedef z_stream *z_streamp; - * indicator */ - #define K_VERS_1_12 (( (1 * 256 + 12) * 256 + 0) * 256 + 0) /* add separate BLOB - * entries */ -- -+#define K_VERS_1_13 (( (1 * 256 + 13) * 256 + 0) * 256 + 0) /* add security label -+ * support */ - /* Newest format we can read */ --#define K_VERS_MAX (( (1 * 256 + 12) * 256 + 255) * 256 + 0) -+#define K_VERS_MAX (( (1 * 256 + 13) * 256 + 255) * 256 + 0) - - - /* Flags to indicate disposition of offsets stored in files */ -@@ -278,6 +279,7 @@ typedef struct _archiveHandle - char *currSchema; /* current schema, or NULL */ - char *currTablespace; /* current tablespace, or NULL */ - bool currWithOids; /* current default_with_oids setting */ -+ bool currWithSecids; /* current default_with_secids setting */ - - void *lo_buf; - size_t lo_buf_used; -@@ -305,6 +307,7 @@ typedef struct _tocEntry - * means use database default */ - char *owner; - bool withOids; /* Used only by "TABLE" tags */ -+ bool withSecids; /* Used only by "TABLE" tags */ - char *desc; - char *defn; - char *dropStmt; -diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c -index f93affd..0a89cf2 100644 ---- a/src/bin/pg_dump/pg_dump.c -+++ b/src/bin/pg_dump/pg_dump.c -@@ -125,7 +125,7 @@ static int binary_upgrade = 0; - static int disable_dollar_quoting = 0; - static int dump_inserts = 0; - static int column_inserts = 0; -- -+static int security_label = 0; - - static void help(const char *progname); - static void expand_schema_name_patterns(SimpleStringList *patterns, -@@ -183,6 +183,11 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, - const char *tag, const char *nspname, const char *owner, - const char *acls); - -+static void dumpSecLabel(Archive *fout, DumpId objDumpId, -+ const char *target, -+ const char *namespace, -+ const char *seclabel); -+ - static void getDependencies(void); - static void getDomainConstraints(TypeInfo *tyinfo); - static void getTableData(TableInfo *tblinfo, int numTables, bool oids); -@@ -215,7 +220,7 @@ static bool binary_upgrade_set_type_oids_by_rel_oid( - static void binary_upgrade_set_relfilenodes(PQExpBuffer upgrade_buffer, - Oid pg_class_oid, bool is_index); - static const char *getAttrName(int attrnum, TableInfo *tblInfo); --static const char *fmtCopyColumnList(const TableInfo *ti); -+static const char *fmtCopyColumnList(const TableInfo *ti, bool secids); - static void do_sql_command(PGconn *conn, const char *query); - static void check_sql_result(PGresult *res, PGconn *conn, const char *query, - ExecStatusType expected); -@@ -299,6 +304,7 @@ main(int argc, char **argv) - {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, - {"role", required_argument, NULL, 3}, - {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, -+ {"security-label", no_argument, &security_label, 1}, - - {NULL, 0, NULL, 0} - }; -@@ -447,6 +453,8 @@ main(int argc, char **argv) - outputNoTablespaces = 1; - else if (strcmp(optarg, "use-set-session-authorization") == 0) - use_setsessauth = 1; -+ else if (strcmp(optarg, "security-label") == 0) -+ security_label = 1; - else - { - fprintf(stderr, -@@ -516,6 +524,10 @@ main(int argc, char **argv) - exit(1); - } - -+ /* Force column insertion mode, when --security-label mode is given. */ -+ if (security_label && dump_inserts) -+ column_inserts = 1; -+ - /* open the output file */ - if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0) - { -@@ -834,6 +846,7 @@ help(const char *progname) - printf(_(" --use-set-session-authorization\n" - " use SET SESSION AUTHORIZATION commands instead of\n" - " ALTER OWNER commands to set ownership\n")); -+ printf(_(" --security-label dump schema/data with security label\n")); - - printf(_("\nConnection options:\n")); - printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); -@@ -1128,7 +1141,7 @@ dumpTableData_copy(Archive *fout, void *dcontext) - * cases involving ADD COLUMN and inheritance.) - */ - if (g_fout->remoteVersion >= 70300) -- column_list = fmtCopyColumnList(tbinfo); -+ column_list = fmtCopyColumnList(tbinfo, tdinfo->secids); - else - column_list = ""; /* can't select columns in COPY */ - -@@ -1252,14 +1265,16 @@ dumpTableData_insert(Archive *fout, void *dcontext) - if (fout->remoteVersion >= 70100) - { - appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " -- "SELECT * FROM ONLY %s", -+ "SELECT %s* FROM ONLY %s", -+ (tdinfo->secids ? "security_label," : ""), - fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, - classname)); - } - else - { - appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " -- "SELECT * FROM %s", -+ "SELECT %s* FROM %s", -+ (tdinfo->secids ? "security_label," : ""), - fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, - classname)); - } -@@ -1399,7 +1414,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) - appendPQExpBuffer(copyBuf, "COPY %s ", - fmtId(tbinfo->dobj.name)); - appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n", -- fmtCopyColumnList(tbinfo), -+ fmtCopyColumnList(tbinfo, tdinfo->secids), - (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : ""); - copyStmt = copyBuf->data; - } -@@ -1413,7 +1428,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) - ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId, - tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, - NULL, tbinfo->rolname, -- false, "TABLE DATA", SECTION_DATA, -+ false, tdinfo->secids, "TABLE DATA", SECTION_DATA, - "", "", copyStmt, - tdinfo->dobj.dependencies, tdinfo->dobj.nDeps, - dumpFn, tdinfo); -@@ -1458,6 +1473,7 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids) - tdinfo->dobj.namespace = tblinfo[i].dobj.namespace; - tdinfo->tdtable = &(tblinfo[i]); - tdinfo->oids = oids; -+ tdinfo->secids = (security_label && tblinfo[i].hassecids) ? true : false; - addObjectDependency(&tdinfo->dobj, tblinfo[i].dobj.dumpId); - - tblinfo[i].dataObj = tdinfo; -@@ -1781,6 +1797,7 @@ dumpDatabase(Archive *AH) - NULL, /* Tablespace */ - dba, /* Owner */ - false, /* with oids */ -+ false, /* with secids */ - "DATABASE", /* Desc */ - SECTION_PRE_DATA, /* Section */ - creaQry->data, /* Create */ -@@ -1826,7 +1843,7 @@ dumpDatabase(Archive *AH) - LargeObjectRelationId); - ArchiveEntry(AH, nilCatalogId, createDumpId(), - "pg_largeobject", NULL, NULL, "", -- false, "pg_largeobject", SECTION_PRE_DATA, -+ false, false, "pg_largeobject", SECTION_PRE_DATA, - loOutQry->data, "", NULL, - NULL, 0, - NULL, NULL); -@@ -1858,7 +1875,7 @@ dumpDatabase(Archive *AH) - appendPQExpBuffer(dbQry, ";\n"); - - ArchiveEntry(AH, dbCatId, createDumpId(), datname, NULL, NULL, -- dba, false, "COMMENT", SECTION_NONE, -+ dba, false, false, "COMMENT", SECTION_NONE, - dbQry->data, "", NULL, - &dbDumpId, 1, NULL, NULL); - } -@@ -1897,7 +1914,7 @@ dumpEncoding(Archive *AH) - - ArchiveEntry(AH, nilCatalogId, createDumpId(), - "ENCODING", NULL, NULL, "", -- false, "ENCODING", SECTION_PRE_DATA, -+ false, false, "ENCODING", SECTION_PRE_DATA, - qry->data, "", NULL, - NULL, 0, - NULL, NULL); -@@ -1924,7 +1941,7 @@ dumpStdStrings(Archive *AH) - - ArchiveEntry(AH, nilCatalogId, createDumpId(), - "STDSTRINGS", NULL, NULL, "", -- false, "STDSTRINGS", SECTION_PRE_DATA, -+ false, false, "STDSTRINGS", SECTION_PRE_DATA, - qry->data, "", NULL, - NULL, 0, - NULL, NULL); -@@ -1957,16 +1974,17 @@ getBlobs(Archive *AH) - /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ - if (AH->remoteVersion >= 90000) - appendPQExpBuffer(blobQry, -- "SELECT oid, (%s lomowner) AS rolname, lomacl" -+ "SELECT oid, (%s lomowner) AS rolname, lomacl, %s" - " FROM pg_largeobject_metadata", -- username_subquery); -+ username_subquery, -+ security_label ? "security_label" : "NULL"); - else if (AH->remoteVersion >= 70100) - appendPQExpBuffer(blobQry, -- "SELECT DISTINCT loid, NULL::oid, NULL::oid" -+ "SELECT DISTINCT loid, NULL::oid, NULL::oid, NULL::text" - " FROM pg_largeobject"); - else - appendPQExpBuffer(blobQry, -- "SELECT oid, NULL::oid, NULL::oid" -+ "SELECT oid, NULL::oid, NULL::oid, NULL::text" - " FROM pg_class WHERE relkind = 'l'"); - - res = PQexec(g_conn, blobQry->data); -@@ -1996,6 +2014,11 @@ getBlobs(Archive *AH) - binfo[i].blobacl = strdup(PQgetvalue(res, i, 2)); - else - binfo[i].blobacl = NULL; -+ -+ if (!PQgetisnull(res, i, 3)) -+ binfo[i].seclabel = strdup(PQgetvalue(res, i, 3)); -+ else -+ binfo[i].seclabel = NULL; - } - - /* -@@ -2035,7 +2058,7 @@ dumpBlob(Archive *AH, BlobInfo *binfo) - ArchiveEntry(AH, binfo->dobj.catId, binfo->dobj.dumpId, - binfo->dobj.name, - NULL, NULL, -- binfo->rolname, false, -+ binfo->rolname, false, false, - "BLOB", SECTION_PRE_DATA, - cquery->data, dquery->data, NULL, - binfo->dobj.dependencies, binfo->dobj.nDeps, -@@ -2050,6 +2073,10 @@ dumpBlob(Archive *AH, BlobInfo *binfo) - NULL, binfo->rolname, - binfo->dobj.catId, 0, binfo->dobj.dumpId); - -+ /* Dump security label if any */ -+ dumpSecLabel(AH, binfo->dobj.dumpId, -+ cquery->data, NULL, binfo->seclabel); -+ - /* Dump ACL if any */ - if (binfo->blobacl) - dumpACL(AH, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT", -@@ -2357,6 +2384,7 @@ getNamespaces(int *numNamespaces) - int i_nspname; - int i_rolname; - int i_nspacl; -+ int i_seclabel; - - /* - * Before 7.3, there are no real namespaces; create two dummy entries, one -@@ -2373,6 +2401,7 @@ getNamespaces(int *numNamespaces) - nsinfo[0].dobj.name = strdup("public"); - nsinfo[0].rolname = strdup(""); - nsinfo[0].nspacl = strdup(""); -+ nsinfo[0].seclabel = strdup(""); - - selectDumpableNamespace(&nsinfo[0]); - -@@ -2383,6 +2412,7 @@ getNamespaces(int *numNamespaces) - nsinfo[1].dobj.name = strdup("pg_catalog"); - nsinfo[1].rolname = strdup(""); - nsinfo[1].nspacl = strdup(""); -+ nsinfo[1].seclabel = strdup(""); - - selectDumpableNamespace(&nsinfo[1]); - -@@ -2403,8 +2433,8 @@ getNamespaces(int *numNamespaces) - */ - appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, " - "(%s nspowner) AS rolname, " -- "nspacl FROM pg_namespace", -- username_subquery); -+ "nspacl, %s AS seclabel FROM pg_namespace", -+ username_subquery, security_label ? "security_label" : "NULL"); - - res = PQexec(g_conn, query->data); - check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); -@@ -2418,6 +2448,7 @@ getNamespaces(int *numNamespaces) - i_nspname = PQfnumber(res, "nspname"); - i_rolname = PQfnumber(res, "rolname"); - i_nspacl = PQfnumber(res, "nspacl"); -+ i_seclabel = PQfnumber(res, "seclabel"); - - for (i = 0; i < ntups; i++) - { -@@ -2428,6 +2459,7 @@ getNamespaces(int *numNamespaces) - nsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_nspname)); - nsinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname)); - nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl)); -+ nsinfo[i].seclabel = strdup(PQgetvalue(res, i, i_seclabel)); - - /* Decide whether to dump this namespace */ - selectDumpableNamespace(&nsinfo[i]); -@@ -2516,6 +2548,7 @@ getTypes(int *numTypes) - int i_typtype; - int i_typisdefined; - int i_isarray; -+ int i_seclabel; - - /* - * we include even the built-in types because those may be used as array -@@ -2548,8 +2581,10 @@ getTypes(int *numTypes) - "typtype, typisdefined, " - "typname[0] = '_' AND typelem != 0 AND " - "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray " -+ ",%s AS seclabel " - "FROM pg_type", -- username_subquery); -+ username_subquery, -+ security_label ? "security_label" : "NULL"); - } - else if (g_fout->remoteVersion >= 70300) - { -@@ -2562,6 +2597,7 @@ getTypes(int *numTypes) - "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " - "typtype, typisdefined, " - "typname[0] = '_' AND typelem != 0 AS isarray " -+ ",NULL AS seclabel " - "FROM pg_type", - username_subquery); - } -@@ -2576,6 +2612,7 @@ getTypes(int *numTypes) - "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " - "typtype, typisdefined, " - "typname[0] = '_' AND typelem != 0 AS isarray " -+ ",NULL AS seclabel " - "FROM pg_type", - username_subquery); - } -@@ -2592,6 +2629,7 @@ getTypes(int *numTypes) - "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " - "typtype, typisdefined, " - "typname[0] = '_' AND typelem != 0 AS isarray " -+ ",NULL AS seclabel " - "FROM pg_type", - username_subquery); - } -@@ -2616,6 +2654,7 @@ getTypes(int *numTypes) - i_typtype = PQfnumber(res, "typtype"); - i_typisdefined = PQfnumber(res, "typisdefined"); - i_isarray = PQfnumber(res, "isarray"); -+ i_seclabel = PQfnumber(res, "seclabel"); - - for (i = 0; i < ntups; i++) - { -@@ -2643,6 +2682,8 @@ getTypes(int *numTypes) - else - tyinfo[i].isArray = false; - -+ tyinfo[i].seclabel = strdup(PQgetvalue(res, i, i_seclabel)); -+ - /* Decide whether we want to dump it */ - selectDumpableType(&tyinfo[i]); - -@@ -3408,6 +3449,7 @@ getTables(int *numTables) - int i_relhasindex; - int i_relhasrules; - int i_relhasoids; -+ int i_relhassecids; - int i_relfrozenxid; - int i_owning_tab; - int i_owning_col; -@@ -3415,6 +3457,7 @@ getTables(int *numTables) - int i_reloptions; - int i_toastreloptions; - int i_reloftype; -+ int i_seclabel; - - /* Make sure we are in proper schema */ - selectSourceSchema("pg_catalog"); -@@ -3451,6 +3494,7 @@ getTables(int *numTables) - "(%s c.relowner) AS rolname, " - "c.relchecks, c.relhastriggers, " - "c.relhasindex, c.relhasrules, c.relhasoids, " -+ "c.relhassecids, " - "c.relfrozenxid, " - "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " - "d.refobjid AS owning_tab, " -@@ -3458,6 +3502,7 @@ getTables(int *numTables) - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "array_to_string(c.reloptions, ', ') AS reloptions, " - "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " -+ ",%s AS seclabel " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -@@ -3468,6 +3513,7 @@ getTables(int *numTables) - "WHERE c.relkind in ('%c', '%c', '%c', '%c') " - "ORDER BY c.oid", - username_subquery, -+ security_label ? "c.security_label" : "NULL", - RELKIND_SEQUENCE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); -@@ -3484,6 +3530,7 @@ getTables(int *numTables) - "(%s c.relowner) AS rolname, " - "c.relchecks, c.relhastriggers, " - "c.relhasindex, c.relhasrules, c.relhasoids, " -+ "false AS relhasecids, " - "c.relfrozenxid, " - "NULL AS reloftype, " - "d.refobjid AS owning_tab, " -@@ -3491,6 +3538,7 @@ getTables(int *numTables) - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "array_to_string(c.reloptions, ', ') AS reloptions, " - "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " -+ ",%s AS seclabel " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -@@ -3501,6 +3549,7 @@ getTables(int *numTables) - "WHERE c.relkind in ('%c', '%c', '%c', '%c') " - "ORDER BY c.oid", - username_subquery, -+ security_label ? "security_label" : "NULL", - RELKIND_SEQUENCE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); -@@ -3517,6 +3566,7 @@ getTables(int *numTables) - "(%s relowner) AS rolname, " - "relchecks, (reltriggers <> 0) AS relhastriggers, " - "relhasindex, relhasrules, relhasoids, " -+ "false AS relhassecids, " - "relfrozenxid, " - "NULL AS reloftype, " - "d.refobjid AS owning_tab, " -@@ -3524,6 +3574,7 @@ getTables(int *numTables) - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "array_to_string(c.reloptions, ', ') AS reloptions, " - "NULL AS toast_reloptions " -+ ",NULL AS seclabel " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -@@ -3549,6 +3600,7 @@ getTables(int *numTables) - "(%s relowner) AS rolname, " - "relchecks, (reltriggers <> 0) AS relhastriggers, " - "relhasindex, relhasrules, relhasoids, " -+ "false AS relhassecids, " - "0 AS relfrozenxid, " - "NULL AS reloftype, " - "d.refobjid AS owning_tab, " -@@ -3556,6 +3608,7 @@ getTables(int *numTables) - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "NULL AS reloptions, " - "NULL AS toast_reloptions " -+ ",NULL AS seclabel " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -@@ -3581,6 +3634,7 @@ getTables(int *numTables) - "(%s relowner) AS rolname, " - "relchecks, (reltriggers <> 0) AS relhastriggers, " - "relhasindex, relhasrules, relhasoids, " -+ "false AS relhassecids, " - "0 AS relfrozenxid, " - "NULL AS reloftype, " - "d.refobjid AS owning_tab, " -@@ -3588,6 +3642,7 @@ getTables(int *numTables) - "NULL AS reltablespace, " - "NULL AS reloptions, " - "NULL AS toast_reloptions " -+ ",NULL AS seclabel " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -@@ -3609,6 +3664,7 @@ getTables(int *numTables) - "(%s relowner) AS rolname, " - "relchecks, (reltriggers <> 0) AS relhastriggers, " - "relhasindex, relhasrules, relhasoids, " -+ "false AS relhassecids, " - "0 AS relfrozenxid, " - "NULL AS reloftype, " - "NULL::oid AS owning_tab, " -@@ -3616,6 +3672,7 @@ getTables(int *numTables) - "NULL AS reltablespace, " - "NULL AS reloptions, " - "NULL AS toast_reloptions " -+ ",NULL AS seclabel " - "FROM pg_class " - "WHERE relkind IN ('%c', '%c', '%c') " - "ORDER BY oid", -@@ -3632,6 +3689,7 @@ getTables(int *numTables) - "relchecks, (reltriggers <> 0) AS relhastriggers, " - "relhasindex, relhasrules, " - "'t'::bool AS relhasoids, " -+ "'f'::bool AS relhassecids, " - "0 AS relfrozenxid, " - "NULL AS reloftype, " - "NULL::oid AS owning_tab, " -@@ -3639,6 +3697,7 @@ getTables(int *numTables) - "NULL AS reltablespace, " - "NULL AS reloptions, " - "NULL AS toast_reloptions " -+ ",NULL AS seclabel " - "FROM pg_class " - "WHERE relkind IN ('%c', '%c', '%c') " - "ORDER BY oid", -@@ -3672,6 +3731,7 @@ getTables(int *numTables) - "NULL AS reltablespace, " - "NULL AS reloptions, " - "NULL AS toast_reloptions " -+ ",NULL AS seclabel " - "FROM pg_class c " - "WHERE relkind IN ('%c', '%c') " - "ORDER BY oid", -@@ -3710,6 +3770,7 @@ getTables(int *numTables) - i_relhasindex = PQfnumber(res, "relhasindex"); - i_relhasrules = PQfnumber(res, "relhasrules"); - i_relhasoids = PQfnumber(res, "relhasoids"); -+ i_relhassecids = PQfnumber(res, "relhassecids"); - i_relfrozenxid = PQfnumber(res, "relfrozenxid"); - i_owning_tab = PQfnumber(res, "owning_tab"); - i_owning_col = PQfnumber(res, "owning_col"); -@@ -3717,6 +3778,7 @@ getTables(int *numTables) - i_reloptions = PQfnumber(res, "reloptions"); - i_toastreloptions = PQfnumber(res, "toast_reloptions"); - i_reloftype = PQfnumber(res, "reloftype"); -+ i_seclabel = PQfnumber(res, "seclabel"); - - if (lockWaitTimeout && g_fout->remoteVersion >= 70300) - { -@@ -3749,6 +3811,7 @@ getTables(int *numTables) - tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0); - tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0); - tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0); -+ tblinfo[i].hassecids = (strcmp(PQgetvalue(res, i, i_relhassecids), "t")==0); - tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid)); - if (PQgetisnull(res, i, i_reloftype)) - tblinfo[i].reloftype = NULL; -@@ -3768,6 +3831,7 @@ getTables(int *numTables) - tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace)); - tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions)); - tblinfo[i].toast_reloptions = strdup(PQgetvalue(res, i, i_toastreloptions)); -+ tblinfo[i].rellabel = strdup(PQgetvalue(res, i, i_seclabel)); - - /* other fields were zeroed above */ - -@@ -5083,6 +5147,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - int i_attalign; - int i_attislocal; - int i_attoptions; -+ int i_seclabel; - PGresult *res; - int ntups; - bool hasdefaults; -@@ -5129,11 +5194,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - "a.attlen, a.attalign, a.attislocal, " - "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " - "array_to_string(attoptions, ', ') AS attoptions " -+ ",%s AS seclabel " - "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " - "ON a.atttypid = t.oid " - "WHERE a.attrelid = '%u'::pg_catalog.oid " - "AND a.attnum > 0::pg_catalog.int2 " - "ORDER BY a.attrelid, a.attnum", -+ security_label ? "a.security_label" : "NULL", - tbinfo->dobj.catId.oid); - } - else if (g_fout->remoteVersion >= 70300) -@@ -5145,6 +5212,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - "a.attlen, a.attalign, a.attislocal, " - "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " - "'' AS attoptions " -+ ",NULL AS seclabel " - "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " - "ON a.atttypid = t.oid " - "WHERE a.attrelid = '%u'::pg_catalog.oid " -@@ -5166,6 +5234,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - "a.attalign, false AS attislocal, " - "format_type(t.oid,a.atttypmod) AS atttypname, " - "'' AS attoptions " -+ ",NULL AS seclabel " - "FROM pg_attribute a LEFT JOIN pg_type t " - "ON a.atttypid = t.oid " - "WHERE a.attrelid = '%u'::oid " -@@ -5184,6 +5253,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - "false AS attislocal, " - "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, " - "'' AS attoptions " -+ ",NULL AS seclabel " - "FROM pg_attribute a " - "WHERE attrelid = '%u'::oid " - "AND attnum > 0::int2 " -@@ -5210,6 +5280,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - i_attalign = PQfnumber(res, "attalign"); - i_attislocal = PQfnumber(res, "attislocal"); - i_attoptions = PQfnumber(res, "attoptions"); -+ i_seclabel = PQfnumber(res, "seclabel"); - - tbinfo->numatts = ntups; - tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); -@@ -5228,6 +5299,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); - tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool)); - tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool)); -+ tbinfo->attlabels = (char **) malloc(ntups * sizeof(char *)); - hasdefaults = false; - - for (j = 0; j < ntups; j++) -@@ -5257,6 +5329,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - tbinfo->inhAttrs[j] = false; - tbinfo->inhAttrDef[j] = false; - tbinfo->inhNotNull[j] = false; -+ tbinfo->attlabels[j] = strdup(PQgetvalue(res, j, i_seclabel)); - } - - PQclear(res); -@@ -6153,7 +6226,7 @@ dumpComment(Archive *fout, const char *target, - */ - ArchiveEntry(fout, nilCatalogId, createDumpId(), - target, namespace, NULL, owner, -- false, "COMMENT", SECTION_NONE, -+ false, false, "COMMENT", SECTION_NONE, - query->data, "", NULL, - &(dumpId), 1, - NULL, NULL); -@@ -6214,7 +6287,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, - target->data, - tbinfo->dobj.namespace->dobj.name, - NULL, tbinfo->rolname, -- false, "COMMENT", SECTION_NONE, -+ false, false, "COMMENT", SECTION_NONE, - query->data, "", NULL, - &(tbinfo->dobj.dumpId), 1, - NULL, NULL); -@@ -6236,7 +6309,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, - target->data, - tbinfo->dobj.namespace->dobj.name, - NULL, tbinfo->rolname, -- false, "COMMENT", SECTION_NONE, -+ false, false, "COMMENT", SECTION_NONE, - query->data, "", NULL, - &(tbinfo->dobj.dumpId), 1, - NULL, NULL); -@@ -6516,7 +6589,7 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) - case DO_BLOB_DATA: - ArchiveEntry(fout, dobj->catId, dobj->dumpId, - dobj->name, NULL, NULL, "", -- false, "BLOBS", SECTION_DATA, -+ false, false, "BLOBS", SECTION_DATA, - "", "", NULL, - dobj->dependencies, dobj->nDeps, - dumpBlobs, NULL); -@@ -6556,7 +6629,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) - nspinfo->dobj.name, - NULL, NULL, - nspinfo->rolname, -- false, "SCHEMA", SECTION_PRE_DATA, -+ false, false, "SCHEMA", SECTION_PRE_DATA, - q->data, delq->data, NULL, - nspinfo->dobj.dependencies, nspinfo->dobj.nDeps, - NULL, NULL); -@@ -6568,6 +6641,9 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) - NULL, nspinfo->rolname, - nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); - -+ dumpSecLabel(fout, nspinfo->dobj.dumpId, -+ q->data, NULL, nspinfo->seclabel); -+ - dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA", - qnspname, NULL, nspinfo->dobj.name, NULL, - nspinfo->rolname, nspinfo->nspacl); -@@ -6685,7 +6761,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) - tyinfo->dobj.name, - tyinfo->dobj.namespace->dobj.name, - NULL, -- tyinfo->rolname, false, -+ tyinfo->rolname, false, false, - "TYPE", SECTION_PRE_DATA, - q->data, delq->data, NULL, - tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, -@@ -6699,6 +6775,12 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) - tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, - tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); - -+ /* Dump type security label */ -+ dumpSecLabel(fout, tyinfo->dobj.dumpId, -+ q->data, -+ tyinfo->dobj.namespace->dobj.name, -+ tyinfo->seclabel); -+ - PQclear(res); - destroyPQExpBuffer(q); - destroyPQExpBuffer(delq); -@@ -7061,7 +7143,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) - tyinfo->dobj.name, - tyinfo->dobj.namespace->dobj.name, - NULL, -- tyinfo->rolname, false, -+ tyinfo->rolname, false, false, - "TYPE", SECTION_PRE_DATA, - q->data, delq->data, NULL, - tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, -@@ -7075,6 +7157,12 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) - tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, - tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); - -+ /* Dump type security label */ -+ dumpSecLabel(fout, tyinfo->dobj.dumpId, -+ q->data, -+ tyinfo->dobj.namespace->dobj.name, -+ tyinfo->seclabel); -+ - PQclear(res); - destroyPQExpBuffer(q); - destroyPQExpBuffer(delq); -@@ -7185,7 +7273,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) - tyinfo->dobj.name, - tyinfo->dobj.namespace->dobj.name, - NULL, -- tyinfo->rolname, false, -+ tyinfo->rolname, false, false, - "DOMAIN", SECTION_PRE_DATA, - q->data, delq->data, NULL, - tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, -@@ -7199,6 +7287,12 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) - tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, - tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); - -+ /* Dump domain security label */ -+ dumpSecLabel(fout, tyinfo->dobj.dumpId, -+ q->data, -+ tyinfo->dobj.namespace->dobj.name, -+ tyinfo->seclabel); -+ - destroyPQExpBuffer(q); - destroyPQExpBuffer(delq); - destroyPQExpBuffer(query); -@@ -7290,7 +7384,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) - tyinfo->dobj.name, - tyinfo->dobj.namespace->dobj.name, - NULL, -- tyinfo->rolname, false, -+ tyinfo->rolname, false, false, - "TYPE", SECTION_PRE_DATA, - q->data, delq->data, NULL, - tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, -@@ -7305,6 +7399,12 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) - tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, - tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); - -+ /* Dump type security label */ -+ dumpSecLabel(fout, tyinfo->dobj.dumpId, -+ q->data, -+ tyinfo->dobj.namespace->dobj.name, -+ tyinfo->seclabel); -+ - PQclear(res); - destroyPQExpBuffer(q); - destroyPQExpBuffer(delq); -@@ -7409,7 +7509,7 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo) - target->data, - tyinfo->dobj.namespace->dobj.name, - NULL, tyinfo->rolname, -- false, "COMMENT", SECTION_NONE, -+ false, false, "COMMENT", SECTION_NONE, - query->data, "", NULL, - &(tyinfo->dobj.dumpId), 1, - NULL, NULL); -@@ -7461,7 +7561,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo) - stinfo->dobj.name, - stinfo->dobj.namespace->dobj.name, - NULL, -- stinfo->baseType->rolname, false, -+ stinfo->baseType->rolname, false, false, - "SHELL TYPE", SECTION_PRE_DATA, - q->data, "", NULL, - stinfo->dobj.dependencies, stinfo->dobj.nDeps, -@@ -7616,7 +7716,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) - ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId, - plang->dobj.name, - lanschema, NULL, plang->lanowner, -- false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA, -+ false, false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA, - defqry->data, delqry->data, NULL, - plang->dobj.dependencies, plang->dobj.nDeps, - NULL, NULL); -@@ -7802,6 +7902,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - char **argmodes = NULL; - char **argnames = NULL; - char **configitems = NULL; -+ char *proseclabel; - int nconfigitems = 0; - int i; - -@@ -7832,8 +7933,10 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "proiswindow, provolatile, proisstrict, prosecdef, " - "proconfig, procost, prorows, " - "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " -+ ",%s AS seclabel " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", -+ security_label ? "security_label" : "NULL", - finfo->dobj.catId.oid); - } - else if (g_fout->remoteVersion >= 80300) -@@ -7845,6 +7948,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "provolatile, proisstrict, prosecdef, " - "proconfig, procost, prorows, " - "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " -+ ",NULL AS seclabel " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); -@@ -7858,6 +7962,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "provolatile, proisstrict, prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " - "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " -+ ",NULL AS seclabel " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); -@@ -7873,6 +7978,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "provolatile, proisstrict, prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " - "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " -+ ",NULL AS seclabel " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); -@@ -7888,6 +7994,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "provolatile, proisstrict, prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " - "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " -+ ",NULL AS seclabel " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); -@@ -7905,6 +8012,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "false AS prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " - "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " -+ ",NULL AS seclabel " - "FROM pg_proc " - "WHERE oid = '%u'::oid", - finfo->dobj.catId.oid); -@@ -7922,6 +8030,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "false AS prosecdef, " - "NULL AS proconfig, 0 AS procost, 0 AS prorows, " - "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " -+ ",NULL AS seclabel " - "FROM pg_proc " - "WHERE oid = '%u'::oid", - finfo->dobj.catId.oid); -@@ -7966,6 +8075,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - procost = PQgetvalue(res, 0, PQfnumber(res, "procost")); - prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); - lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); -+ proseclabel = PQgetvalue(res, 0, PQfnumber(res, "seclabel")); - - /* - * See backend/commands/functioncmds.c for details of how the 'AS' clause -@@ -8176,7 +8286,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - funcsig_tag, - finfo->dobj.namespace->dobj.name, - NULL, -- finfo->rolname, false, -+ finfo->rolname, false, false, - "FUNCTION", SECTION_PRE_DATA, - q->data, delqry->data, NULL, - finfo->dobj.dependencies, finfo->dobj.nDeps, -@@ -8189,6 +8299,12 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - finfo->dobj.namespace->dobj.name, finfo->rolname, - finfo->dobj.catId, 0, finfo->dobj.dumpId); - -+ /* Dump Function security label */ -+ dumpSecLabel(fout, finfo->dobj.dumpId, -+ q->data, -+ finfo->dobj.namespace->dobj.name, -+ proseclabel); -+ - dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION", - funcsig, NULL, funcsig_tag, - finfo->dobj.namespace->dobj.name, -@@ -8330,7 +8446,7 @@ dumpCast(Archive *fout, CastInfo *cast) - ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId, - castsig->data, - "pg_catalog", NULL, "", -- false, "CAST", SECTION_PRE_DATA, -+ false, false, "CAST", SECTION_PRE_DATA, - defqry->data, delqry->data, NULL, - cast->dobj.dependencies, cast->dobj.nDeps, - NULL, NULL); -@@ -8574,7 +8690,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) - oprinfo->dobj.namespace->dobj.name, - NULL, - oprinfo->rolname, -- false, "OPERATOR", SECTION_PRE_DATA, -+ false, false, "OPERATOR", SECTION_PRE_DATA, - q->data, delq->data, NULL, - oprinfo->dobj.dependencies, oprinfo->dobj.nDeps, - NULL, NULL); -@@ -9033,7 +9149,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) - opcinfo->dobj.namespace->dobj.name, - NULL, - opcinfo->rolname, -- false, "OPERATOR CLASS", SECTION_PRE_DATA, -+ false, false, "OPERATOR CLASS", SECTION_PRE_DATA, - q->data, delq->data, NULL, - opcinfo->dobj.dependencies, opcinfo->dobj.nDeps, - NULL, NULL); -@@ -9314,7 +9430,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) - opfinfo->dobj.namespace->dobj.name, - NULL, - opfinfo->rolname, -- false, "OPERATOR FAMILY", SECTION_PRE_DATA, -+ false, false, "OPERATOR FAMILY", SECTION_PRE_DATA, - q->data, delq->data, NULL, - opfinfo->dobj.dependencies, opfinfo->dobj.nDeps, - NULL, NULL); -@@ -9430,7 +9546,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) - convinfo->dobj.namespace->dobj.name, - NULL, - convinfo->rolname, -- false, "CONVERSION", SECTION_PRE_DATA, -+ false, false, "CONVERSION", SECTION_PRE_DATA, - q->data, delq->data, NULL, - convinfo->dobj.dependencies, convinfo->dobj.nDeps, - NULL, NULL); -@@ -9511,11 +9627,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo) - int i_aggtranstype; - int i_agginitval; - int i_convertok; -+ int i_seclabel; - const char *aggtransfn; - const char *aggfinalfn; - const char *aggsortop; - const char *aggtranstype; - const char *agginitval; -+ const char *seclabel; - bool convertok; - - /* Skip if not to be dumped */ -@@ -9538,9 +9656,11 @@ dumpAgg(Archive *fout, AggInfo *agginfo) - "aggsortop::pg_catalog.regoperator, " - "agginitval, " - "'t'::boolean AS convertok " -+ ",%s AS seclabel " - "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " - "WHERE a.aggfnoid = p.oid " - "AND p.oid = '%u'::pg_catalog.oid", -+ security_label ? "p.security_label" : "NULL", - agginfo->aggfn.dobj.catId.oid); - } - else if (g_fout->remoteVersion >= 70300) -@@ -9550,6 +9670,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) - "0 AS aggsortop, " - "agginitval, " - "'t'::boolean AS convertok " -+ ",NULL AS seclabel " - "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " - "WHERE a.aggfnoid = p.oid " - "AND p.oid = '%u'::pg_catalog.oid", -@@ -9562,6 +9683,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) - "0 AS aggsortop, " - "agginitval, " - "'t'::boolean AS convertok " -+ ",NULL AS seclabel " - "FROM pg_aggregate " - "WHERE oid = '%u'::oid", - agginfo->aggfn.dobj.catId.oid); -@@ -9574,6 +9696,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) - "0 AS aggsortop, " - "agginitval1 AS agginitval, " - "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok " -+ ",NULL AS seclabel " - "FROM pg_aggregate " - "WHERE oid = '%u'::oid", - agginfo->aggfn.dobj.catId.oid); -@@ -9599,6 +9722,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) - i_aggtranstype = PQfnumber(res, "aggtranstype"); - i_agginitval = PQfnumber(res, "agginitval"); - i_convertok = PQfnumber(res, "convertok"); -+ i_seclabel = PQfnumber(res, "seclabel"); - - aggtransfn = PQgetvalue(res, 0, i_aggtransfn); - aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn); -@@ -9606,6 +9730,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) - aggtranstype = PQgetvalue(res, 0, i_aggtranstype); - agginitval = PQgetvalue(res, 0, i_agginitval); - convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't'); -+ seclabel = PQgetvalue(res, 0, i_seclabel); - - aggsig = format_aggregate_signature(agginfo, fout, true); - aggsig_tag = format_aggregate_signature(agginfo, fout, false); -@@ -9674,7 +9799,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) - agginfo->aggfn.dobj.namespace->dobj.name, - NULL, - agginfo->aggfn.rolname, -- false, "AGGREGATE", SECTION_PRE_DATA, -+ false, false, "AGGREGATE", SECTION_PRE_DATA, - q->data, delq->data, NULL, - agginfo->aggfn.dobj.dependencies, agginfo->aggfn.dobj.nDeps, - NULL, NULL); -@@ -9686,6 +9811,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo) - agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname, - agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId); - -+ /* Dump Aggregate security label */ -+ dumpSecLabel(fout, agginfo->aggfn.dobj.dumpId, -+ q->data, -+ agginfo->aggfn.dobj.namespace->dobj.name, -+ seclabel); -+ - /* - * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL - * command look like a function's GRANT; in particular this affects the -@@ -9762,7 +9893,7 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) - prsinfo->dobj.namespace->dobj.name, - NULL, - "", -- false, "TEXT SEARCH PARSER", SECTION_PRE_DATA, -+ false, false, "TEXT SEARCH PARSER", SECTION_PRE_DATA, - q->data, delq->data, NULL, - prsinfo->dobj.dependencies, prsinfo->dobj.nDeps, - NULL, NULL); -@@ -9854,7 +9985,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) - dictinfo->dobj.namespace->dobj.name, - NULL, - dictinfo->rolname, -- false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA, -+ false, false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA, - q->data, delq->data, NULL, - dictinfo->dobj.dependencies, dictinfo->dobj.nDeps, - NULL, NULL); -@@ -9914,7 +10045,7 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) - tmplinfo->dobj.namespace->dobj.name, - NULL, - "", -- false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA, -+ false, false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA, - q->data, delq->data, NULL, - tmplinfo->dobj.dependencies, tmplinfo->dobj.nDeps, - NULL, NULL); -@@ -10047,7 +10178,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) - cfginfo->dobj.namespace->dobj.name, - NULL, - cfginfo->rolname, -- false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA, -+ false, false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA, - q->data, delq->data, NULL, - cfginfo->dobj.dependencies, cfginfo->dobj.nDeps, - NULL, NULL); -@@ -10103,7 +10234,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) - NULL, - NULL, - fdwinfo->rolname, -- false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA, -+ false, false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA, - q->data, delq->data, NULL, - fdwinfo->dobj.dependencies, fdwinfo->dobj.nDeps, - NULL, NULL); -@@ -10191,7 +10322,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) - NULL, - NULL, - srvinfo->rolname, -- false, "SERVER", SECTION_PRE_DATA, -+ false, false, "SERVER", SECTION_PRE_DATA, - q->data, delq->data, NULL, - srvinfo->dobj.dependencies, srvinfo->dobj.nDeps, - NULL, NULL); -@@ -10296,7 +10427,7 @@ dumpUserMappings(Archive *fout, - tag->data, - namespace, - NULL, -- owner, false, -+ owner, false, false, - "USER MAPPING", SECTION_PRE_DATA, - q->data, delq->data, NULL, - &dumpId, 1, -@@ -10367,7 +10498,7 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) - daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL, - NULL, - daclinfo->defaclrole, -- false, "DEFAULT ACL", SECTION_NONE, -+ false, false, "DEFAULT ACL", SECTION_NONE, - q->data, "", NULL, - daclinfo->dobj.dependencies, daclinfo->dobj.nDeps, - NULL, NULL); -@@ -10424,7 +10555,7 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, - tag, nspname, - NULL, - owner ? owner : "", -- false, "ACL", SECTION_NONE, -+ false, false, "ACL", SECTION_NONE, - sql->data, "", NULL, - &(objDumpId), 1, - NULL, NULL); -@@ -10433,6 +10564,123 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, - } - - /* -+ * dumpSecLabel -+ * -+ * write out security label of the objects -+ */ -+static void -+dumpSecLabel(Archive *fout, DumpId objDumpId, -+ const char *target, const char *namespace, -+ const char *seclabel) -+{ -+ PQExpBuffer qry; -+ -+ /* Do nothing, if security label dump is not given */ -+ if (!security_label || strlen(seclabel) == 0) -+ return; -+ -+ /* Do nothing, if --data-only for schemas, or --schema-only for blobs */ -+ if (strncmp(target, "LARGE OBJECT", 13) == 0) -+ { -+ if (schemaOnly) -+ return; -+ } -+ else -+ { -+ if (dataOnly) -+ return; -+ } -+ -+ /* Make ALTER xxx SECURITY LABEL TO command */ -+ qry = createPQExpBuffer(); -+ -+ appendPQExpBuffer(qry, "ALTER %s SECURITY LABEL TO '%s';", -+ target, seclabel); -+ -+ ArchiveEntry(fout, -+ nilCatalogId, /* catalog ID */ -+ createDumpId(), /* dump ID */ -+ target, /* name */ -+ namespace, /* namespace */ -+ NULL, /* tablespace */ -+ "", /* owner */ -+ false, /* with oids */ -+ false, /* with secids */ -+ "LABEL", /* desc */ -+ SECTION_NONE, /* section */ -+ qry->data, /* create */ -+ "", /* delete*/ -+ NULL, /* copy */ -+ &(objDumpId), /* dependency */ -+ 1, /* # deps */ -+ NULL, /* dumper Func */ -+ NULL); /* dumper Arg */ -+ -+ destroyPQExpBuffer(qry); -+} -+ -+/* -+ * dumpTableSecLabel -+ * -+ * write out security label of the table -+ */ -+static void -+dumpTableSecLabel(Archive *fout, DumpId objDumpId, -+ const char *namespace, -+ const char *table_name, -+ const char **column_names, int ncolumns, -+ const char *table_label, const char **column_labels) -+{ -+ PQExpBuffer qry; -+ PQExpBuffer tag; -+ int i; -+ -+ if (!security_label || dataOnly) -+ return; -+ -+ /* Make ALTER xxx SECURITY LABEL TO command */ -+ qry = createPQExpBuffer(); -+ tag = createPQExpBuffer(); -+ -+ appendPQExpBuffer(tag, "TABLE %s", table_name); -+ -+ if (strlen(table_label) > 0) -+ appendPQExpBuffer(qry, "ALTER TABLE %s SECURITY LABEL TO '%s';\n", -+ table_name, table_label); -+ for (i = 0; i < ncolumns; i++) -+ { -+ if (strlen(column_labels[i]) > 0) -+ appendPQExpBuffer(qry, "ALTER TABLE %s ALTER %s SECURITY LABEL TO '%s';\n", -+ table_name, -+ column_names[i], -+ column_labels[i]); -+ } -+ -+ if (qry->len > 0) -+ ArchiveEntry(fout, -+ nilCatalogId, /* catalog ID */ -+ createDumpId(), /* dump ID */ -+ tag->data, /* name */ -+ namespace, /* namespace */ -+ NULL, /* tablespace */ -+ "", /* owner */ -+ false, /* with oids */ -+ false, /* with secids */ -+ "LABEL", /* desc */ -+ SECTION_NONE, /* section */ -+ qry->data, /* create */ -+ "", /* delete*/ -+ NULL, /* copy */ -+ &(objDumpId), /* dependency */ -+ 1, /* # deps */ -+ NULL, /* dumper Func */ -+ NULL); /* dumper Arg */ -+ -+ destroyPQExpBuffer(qry); -+ destroyPQExpBuffer(tag); -+} -+ -+/* - * dumpTable - * write out to fout the declarations (not data) of a user-defined table - */ -@@ -10588,7 +10836,6 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) - - appendPQExpBuffer(q, "CREATE VIEW %s AS\n %s\n", - fmtId(tbinfo->dobj.name), viewdef); -- - PQclear(res); - } - else -@@ -10939,6 +11186,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) - (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace, - tbinfo->rolname, - (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false, -+ (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hassecids : false, - reltypename, SECTION_PRE_DATA, - q->data, delq->data, NULL, - tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, -@@ -10959,6 +11207,27 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) - dumpTableConstraintComment(fout, constr); - } - -+ /* Dump relation/attribute's security labels */ -+ if (tbinfo->relkind == RELKIND_RELATION) -+ { -+ dumpTableSecLabel(fout, tbinfo->dobj.dumpId, -+ tbinfo->dobj.namespace->dobj.name, -+ tbinfo->dobj.name, -+ tbinfo->attnames, tbinfo->numatts, -+ tbinfo->rellabel, tbinfo->attlabels); -+ } -+ else -+ { -+ PQExpBuffer target = createPQExpBuffer(); -+ -+ appendPQExpBuffer(target, "%s %s", reltypename, fmtId(tbinfo->dobj.name)); -+ dumpSecLabel(fout, tbinfo->dobj.dumpId, -+ target->data, -+ tbinfo->dobj.namespace->dobj.name, -+ tbinfo->rellabel); -+ destroyPQExpBuffer(target); -+ } -+ - destroyPQExpBuffer(query); - destroyPQExpBuffer(q); - destroyPQExpBuffer(delq); -@@ -11007,7 +11276,7 @@ dumpAttrDef(Archive *fout, AttrDefInfo *adinfo) - tbinfo->dobj.namespace->dobj.name, - NULL, - tbinfo->rolname, -- false, "DEFAULT", SECTION_PRE_DATA, -+ false, false, "DEFAULT", SECTION_PRE_DATA, - q->data, delq->data, NULL, - adinfo->dobj.dependencies, adinfo->dobj.nDeps, - NULL, NULL); -@@ -11103,7 +11372,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) - indxinfo->dobj.name, - tbinfo->dobj.namespace->dobj.name, - indxinfo->tablespace, -- tbinfo->rolname, false, -+ tbinfo->rolname, false, false, - "INDEX", SECTION_POST_DATA, - q->data, delq->data, NULL, - indxinfo->dobj.dependencies, indxinfo->dobj.nDeps, -@@ -11228,7 +11497,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) - coninfo->dobj.name, - tbinfo->dobj.namespace->dobj.name, - indxinfo->tablespace, -- tbinfo->rolname, false, -+ tbinfo->rolname, false, false, - "CONSTRAINT", SECTION_POST_DATA, - q->data, delq->data, NULL, - coninfo->dobj.dependencies, coninfo->dobj.nDeps, -@@ -11261,7 +11530,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) - coninfo->dobj.name, - tbinfo->dobj.namespace->dobj.name, - NULL, -- tbinfo->rolname, false, -+ tbinfo->rolname, false, false, - "FK CONSTRAINT", SECTION_POST_DATA, - q->data, delq->data, NULL, - coninfo->dobj.dependencies, coninfo->dobj.nDeps, -@@ -11296,7 +11565,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) - coninfo->dobj.name, - tbinfo->dobj.namespace->dobj.name, - NULL, -- tbinfo->rolname, false, -+ tbinfo->rolname, false, false, - "CHECK CONSTRAINT", SECTION_POST_DATA, - q->data, delq->data, NULL, - coninfo->dobj.dependencies, coninfo->dobj.nDeps, -@@ -11332,7 +11601,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) - coninfo->dobj.name, - tyinfo->dobj.namespace->dobj.name, - NULL, -- tyinfo->rolname, false, -+ tyinfo->rolname, false, false, - "CHECK CONSTRAINT", SECTION_POST_DATA, - q->data, delq->data, NULL, - coninfo->dobj.dependencies, coninfo->dobj.nDeps, -@@ -11618,7 +11887,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) - tbinfo->dobj.namespace->dobj.name, - NULL, - tbinfo->rolname, -- false, "SEQUENCE", SECTION_PRE_DATA, -+ false, false, "SEQUENCE", SECTION_PRE_DATA, - query->data, delqry->data, NULL, - tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, - NULL, NULL); -@@ -11654,7 +11923,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) - tbinfo->dobj.namespace->dobj.name, - NULL, - tbinfo->rolname, -- false, "SEQUENCE OWNED BY", SECTION_PRE_DATA, -+ false, false, "SEQUENCE OWNED BY", SECTION_PRE_DATA, - query->data, "", NULL, - &(tbinfo->dobj.dumpId), 1, - NULL, NULL); -@@ -11667,6 +11936,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) - dumpComment(fout, query->data, - tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, - tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); -+ -+ /* Dump sequence security label */ -+ dumpSecLabel(fout, tbinfo->dobj.dumpId, -+ query->data, -+ tbinfo->dobj.namespace->dobj.name, -+ tbinfo->rellabel); - } - - if (!schemaOnly) -@@ -11682,7 +11957,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) - tbinfo->dobj.namespace->dobj.name, - NULL, - tbinfo->rolname, -- false, "SEQUENCE SET", SECTION_PRE_DATA, -+ false, false, "SEQUENCE SET", SECTION_PRE_DATA, - query->data, "", NULL, - &(tbinfo->dobj.dumpId), 1, - NULL, NULL); -@@ -11866,7 +12141,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) - tginfo->dobj.name, - tbinfo->dobj.namespace->dobj.name, - NULL, -- tbinfo->rolname, false, -+ tbinfo->rolname, false, false, - "TRIGGER", SECTION_POST_DATA, - query->data, delqry->data, NULL, - tginfo->dobj.dependencies, tginfo->dobj.nDeps, -@@ -11986,7 +12261,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) - rinfo->dobj.name, - tbinfo->dobj.namespace->dobj.name, - NULL, -- tbinfo->rolname, false, -+ tbinfo->rolname, false, false, - "RULE", SECTION_POST_DATA, - cmd->data, delcmd->data, NULL, - rinfo->dobj.dependencies, rinfo->dobj.nDeps, -@@ -12351,7 +12626,7 @@ fmtQualifiedId(const char *schema, const char *id) - * "", not an invalid "()" column list. - */ - static const char * --fmtCopyColumnList(const TableInfo *ti) -+fmtCopyColumnList(const TableInfo *ti, bool secids) - { - static PQExpBuffer q = NULL; - int numatts = ti->numatts; -@@ -12367,6 +12642,11 @@ fmtCopyColumnList(const TableInfo *ti) - - appendPQExpBuffer(q, "("); - needComma = false; -+ if (secids) -+ { -+ appendPQExpBuffer(q, "security_label"); -+ needComma = true; -+ } - for (i = 0; i < numatts; i++) - { - if (attisdropped[i]) -diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h -index c309f69..2a35392 100644 ---- a/src/bin/pg_dump/pg_dump.h -+++ b/src/bin/pg_dump/pg_dump.h -@@ -137,6 +137,7 @@ typedef struct _namespaceInfo - DumpableObject dobj; - char *rolname; /* name of owner, or empty string */ - char *nspacl; -+ char *seclabel; - } NamespaceInfo; - - typedef struct _typeInfo -@@ -153,6 +154,7 @@ typedef struct _typeInfo - char typrelkind; /* 'r', 'v', 'c', etc */ - char typtype; /* 'b', 'c', etc */ - bool isArray; /* true if auto-generated array type */ -+ char *seclabel; - bool isDefined; /* true if typisdefined */ - /* If it's a dumpable base type, we create a "shell type" entry for it */ - struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */ -@@ -227,9 +229,11 @@ typedef struct _tableInfo - bool hasrules; /* does it have any rules? */ - bool hastriggers; /* does it have any triggers? */ - bool hasoids; /* does it have OIDs? */ -+ bool hassecids; /* does it have security-Id? */ - uint32 frozenxid; /* for restore frozen xid */ - int ncheck; /* # of CHECK expressions */ - char *reloftype; /* underlying type for typed table */ -+ char *rellabel; /* relation's security label */ - /* these two are set only if table is a sequence owned by a column: */ - Oid owning_tab; /* OID of table owning sequence */ - int owning_col; /* attr # of column owning sequence */ -@@ -252,6 +256,7 @@ typedef struct _tableInfo - char *attalign; /* attribute align, used by binary_upgrade */ - bool *attislocal; /* true if attr has local definition */ - char **attoptions; /* per-attribute options */ -+ char **attlabels; /* attribute's security label */ - - /* - * Note: we need to store per-attribute notnull, default, and constraint -@@ -287,6 +292,7 @@ typedef struct _tableDataInfo - DumpableObject dobj; - TableInfo *tdtable; /* link to table to dump */ - bool oids; /* include OIDs in data? */ -+ bool secids; /* include SecIDs in data? */ - } TableDataInfo; - - typedef struct _indxInfo -@@ -448,6 +454,7 @@ typedef struct _blobInfo - DumpableObject dobj; - char *rolname; - char *blobacl; -+ char *seclabel; - } BlobInfo; - - /* global decls */ -diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c -index 85032a9..3e4aad6 100644 ---- a/src/bin/pg_dump/pg_dumpall.c -+++ b/src/bin/pg_dump/pg_dumpall.c -@@ -69,6 +69,7 @@ static int disable_triggers = 0; - static int inserts = 0; - static int no_tablespaces = 0; - static int use_setsessauth = 0; -+static int security_label = 0; - static int server_version; - - static FILE *OPF; -@@ -132,6 +133,7 @@ main(int argc, char *argv[]) - {"no-tablespaces", no_argument, &no_tablespaces, 1}, - {"role", required_argument, NULL, 3}, - {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, -+ {"security-label", no_argument, &security_label, 1}, - - {NULL, 0, NULL, 0} - }; -@@ -285,6 +287,8 @@ main(int argc, char *argv[]) - no_tablespaces = 1; - else if (strcmp(optarg, "use-set-session-authorization") == 0) - use_setsessauth = 1; -+ else if (strcmp(optarg, "security-label") == 0) -+ security_label = 1; - else - { - fprintf(stderr, -@@ -368,6 +372,8 @@ main(int argc, char *argv[]) - appendPQExpBuffer(pgdumpopts, " --no-tablespaces"); - if (use_setsessauth) - appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); -+ if (security_label) -+ appendPQExpBuffer(pgdumpopts, " --security-label"); - - /* - * If there was a database specified on the command line, use that, -@@ -562,6 +568,7 @@ help(void) - printf(_(" --use-set-session-authorization\n" - " use SET SESSION AUTHORIZATION commands instead of\n" - " ALTER OWNER commands to set ownership\n")); -+ printf(_(" --security-label dump schema/data with security label\n")); - - printf(_("\nConnection options:\n")); - printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); -@@ -950,6 +957,7 @@ dropTablespaces(PGconn *conn) - static void - dumpTablespaces(PGconn *conn) - { -+ PQExpBuffer qry = createPQExpBuffer(); - PGresult *res; - int i; - -@@ -958,31 +966,37 @@ dumpTablespaces(PGconn *conn) - * pg_xxx) - */ - if (server_version >= 90000) -- res = executeQuery(conn, "SELECT spcname, " -+ appendPQExpBuffer(qry, "SELECT spcname, " - "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " - "spclocation, spcacl, " - "array_to_string(spcoptions, ', ')," - "pg_catalog.shobj_description(oid, 'pg_tablespace') " -+ ",%s AS seclabel " - "FROM pg_catalog.pg_tablespace " - "WHERE spcname !~ '^pg_' " -- "ORDER BY 1"); -+ "ORDER BY 1", -+ security_label ? "security_label" : "NULL"); - else if (server_version >= 80200) -- res = executeQuery(conn, "SELECT spcname, " -+ appendPQExpBuffer(qry, "SELECT spcname, " - "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " - "spclocation, spcacl, null, " - "pg_catalog.shobj_description(oid, 'pg_tablespace') " -+ ",NULL AS seclabel" - "FROM pg_catalog.pg_tablespace " - "WHERE spcname !~ '^pg_' " - "ORDER BY 1"); - else -- res = executeQuery(conn, "SELECT spcname, " -+ appendPQExpBuffer(qry, "SELECT spcname, " - "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " - "spclocation, spcacl, " - "null, null " -+ ",NULL AS seclabel" - "FROM pg_catalog.pg_tablespace " - "WHERE spcname !~ '^pg_' " - "ORDER BY 1"); - -+ res = PQexec(conn, qry->data); -+ - if (PQntuples(res) > 0) - fprintf(OPF, "--\n-- Tablespaces\n--\n\n"); - -@@ -995,6 +1009,7 @@ dumpTablespaces(PGconn *conn) - char *spcacl = PQgetvalue(res, i, 3); - char *spcoptions = PQgetvalue(res, i, 4); - char *spccomment = PQgetvalue(res, i, 5); -+ char *spcseclabel = PQgetvalue(res, i, 6); - char *fspcname; - - /* needed for buildACLCommands() */ -@@ -1011,6 +1026,10 @@ dumpTablespaces(PGconn *conn) - appendPQExpBuffer(buf, "ALTER TABLESPACE %s SET (%s);\n", - fspcname, spcoptions); - -+ if (security_label && strlen(spcseclabel) > 0) -+ appendPQExpBuffer(buf, "ALTER TABLESPACE %s SECURITY LABEL TO '%s';\n", -+ fspcname, spcseclabel); -+ - if (!skip_acls && - !buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner, - "", server_version, buf)) -@@ -1154,48 +1173,48 @@ dumpCreateDB(PGconn *conn) - - /* Now collect all the information about databases to dump */ - if (server_version >= 80400) -- res = executeQuery(conn, -- "SELECT datname, " -+ appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "datcollate, datctype, datfrozenxid, " - "datistemplate, datacl, datconnlimit, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " -+ ",%s AS seclabel " - "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " -- "WHERE datallowconn ORDER BY 1"); -+ "WHERE datallowconn ORDER BY 1", -+ security_label ? "d.security_label" : "NULL"); - else if (server_version >= 80100) -- res = executeQuery(conn, -- "SELECT datname, " -+ appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, datfrozenxid, " - "datistemplate, datacl, datconnlimit, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " -+ ",NULL AS seclabel " - "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 80000) -- res = executeQuery(conn, -- "SELECT datname, " -+ appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, datfrozenxid, " - "datistemplate, datacl, -1 as datconnlimit, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " -+ ",NULL AS seclabel " - "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 70300) -- res = executeQuery(conn, -- "SELECT datname, " -+ appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, datfrozenxid, " - "datistemplate, datacl, -1 as datconnlimit, " - "'pg_default' AS dattablespace " -+ ",NULL AS seclabel " - "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 70100) -- res = executeQuery(conn, -- "SELECT datname, " -+ appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(" - "(select usename from pg_shadow where usesysid=datdba), " - "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " -@@ -1203,6 +1222,7 @@ dumpCreateDB(PGconn *conn) - "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " - "datistemplate, '' as datacl, -1 as datconnlimit, " - "'pg_default' AS dattablespace " -+ ",NULL AS seclabel " - "FROM pg_database d " - "WHERE datallowconn ORDER BY 1"); - else -@@ -1211,18 +1231,20 @@ dumpCreateDB(PGconn *conn) - * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal - * with getting a NULL by not printing any OWNER clause. - */ -- res = executeQuery(conn, -- "SELECT datname, " -+ appendPQExpBuffer(buf, "SELECT datname, " - "(select usename from pg_shadow where usesysid=datdba), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " - "'f' as datistemplate, " - "'' as datacl, -1 as datconnlimit, " - "'pg_default' AS dattablespace " -+ ",NULL AS seclabel " - "FROM pg_database d " - "ORDER BY 1"); - } - -+ res = PQexec(conn, buf->data); -+ - for (i = 0; i < PQntuples(res); i++) - { - char *dbname = PQgetvalue(res, i, 0); -@@ -1235,6 +1257,7 @@ dumpCreateDB(PGconn *conn) - char *dbacl = PQgetvalue(res, i, 7); - char *dbconnlimit = PQgetvalue(res, i, 8); - char *dbtablespace = PQgetvalue(res, i, 9); -+ char *dbseclabel = PQgetvalue(res, i, 10); - char *fdbname; - - fdbname = strdup(fmtId(dbname)); -@@ -1310,6 +1333,9 @@ dumpCreateDB(PGconn *conn) - appendPQExpBuffer(buf, ";\n"); - } - } -+ if (security_label && strlen(dbseclabel) > 0) -+ appendPQExpBuffer(buf, "ALTER DATABASE %s SECURITY LABEL TO '%s';\n", -+ dbname, dbseclabel); - - if (!skip_acls && - !buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner, -diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c -index dbdf7ac..03b9d67 100644 ---- a/src/bin/pg_dump/pg_restore.c -+++ b/src/bin/pg_dump/pg_restore.c -@@ -76,6 +76,7 @@ main(int argc, char **argv) - static int no_data_for_failed_tables = 0; - static int outputNoTablespaces = 0; - static int use_setsessauth = 0; -+ static int no_security_label = 0; - - struct option cmdopts[] = { - {"clean", 0, NULL, 'c'}, -@@ -116,6 +117,7 @@ main(int argc, char **argv) - {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, - {"role", required_argument, NULL, 2}, - {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, -+ {"no-security-label", no_argument, &no_security_label, 1}, - - {NULL, 0, NULL, 0} - }; -@@ -262,6 +264,8 @@ main(int argc, char **argv) - outputNoTablespaces = 1; - else if (strcmp(optarg, "use-set-session-authorization") == 0) - use_setsessauth = 1; -+ else if (strcmp(optarg, "no-security-label") == 0) -+ no_security_label = 1; - else - { - fprintf(stderr, -@@ -337,6 +341,7 @@ main(int argc, char **argv) - opts->noDataForFailedTables = no_data_for_failed_tables; - opts->noTablespace = outputNoTablespaces; - opts->use_setsessauth = use_setsessauth; -+ opts->noSecLabel = no_security_label; - - if (opts->formatName) - { -@@ -448,6 +453,7 @@ usage(const char *progname) - " ALTER OWNER commands to set ownership\n")); - printf(_(" -1, --single-transaction\n" - " restore as a single transaction\n")); -+ printf(_(" --no-security-label skip restoration of security labels\n")); - - printf(_("\nConnection options:\n")); - printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); -diff --git a/src/include/access/htup.h b/src/include/access/htup.h -index 3be701b..4501289 100644 ---- a/src/include/access/htup.h -+++ b/src/include/access/htup.h -@@ -163,7 +163,7 @@ typedef HeapTupleHeaderData *HeapTupleHeader; - #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ - #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ - #define HEAP_HASOID 0x0008 /* has an object-id field */ --/* bit 0x0010 is available */ -+#define HEAP_HASSECID 0x0010 /* has an security-id field */ - #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ - #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ - #define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */ -@@ -292,6 +292,9 @@ do { \ - (tup)->t_choice.t_datum.datum_typmod = (typmod) \ - ) - -+#define HeapTupleHeaderHasOid(tup) \ -+ ((tup)->t_infomask & HEAP_HASOID) -+ - #define HeapTupleHeaderGetOid(tup) \ - ( \ - ((tup)->t_infomask & HEAP_HASOID) ? \ -@@ -351,6 +354,25 @@ do { \ - (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ - ) - -+#define HeapTupleHeaderHasSecid(tup) \ -+ ((tup)->t_infomask & HEAP_HASSECID) -+ -+#define HeapTupleHeaderGetSecid(tup) \ -+ ( \ -+ HeapTupleHeaderHasSecid(tup) \ -+ ? (*(Oid *)((char *)(tup) + (tup)->t_hoff \ -+ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ -+ - sizeof(Oid))) \ -+ : InvalidOid \ -+ ) -+ -+#define HeapTupleHeaderSetSecid(tup, secid) \ -+ do { \ -+ Assert(HeapTupleHeaderHasSecid(tup)); \ -+ *((Oid *)((char *)(tup) + (tup)->t_hoff \ -+ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ -+ - sizeof(Oid))) = (secid); \ -+ } while(0) - - /* - * BITMAPLEN(NATTS) - -@@ -545,12 +567,23 @@ typedef HeapTupleData *HeapTuple; - #define HeapTupleClearHeapOnly(tuple) \ - HeapTupleHeaderClearHeapOnly((tuple)->t_data) - -+#define HeapTupleHasOid(tuple) \ -+ HeapTupleHeaderHasOid((tuple)->t_data) -+ - #define HeapTupleGetOid(tuple) \ - HeapTupleHeaderGetOid((tuple)->t_data) - - #define HeapTupleSetOid(tuple, oid) \ - HeapTupleHeaderSetOid((tuple)->t_data, (oid)) - -+#define HeapTupleHasSecid(tuple) \ -+ HeapTupleHeaderHasSecid((tuple)->t_data) -+ -+#define HeapTupleGetSecid(tuple) \ -+ HeapTupleHeaderGetSecid((tuple)->t_data) -+ -+#define HeapTupleSetSecid(tuple, secid) \ -+ HeapTupleHeaderSetSecid((tuple)->t_data, (secid)) - - /* - * WAL record definitions for heapam.c's WAL operations -diff --git a/src/include/access/sysattr.h b/src/include/access/sysattr.h -index 6047735..ce9a994 100644 ---- a/src/include/access/sysattr.h -+++ b/src/include/access/sysattr.h -@@ -25,7 +25,8 @@ - #define MaxTransactionIdAttributeNumber (-5) - #define MaxCommandIdAttributeNumber (-6) - #define TableOidAttributeNumber (-7) --#define FirstLowInvalidHeapAttributeNumber (-8) -+#define SecurityLabelAttributeNumber (-8) -+#define FirstLowInvalidHeapAttributeNumber (-9) - - - #endif /* SYSATTR_H */ -diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h -index e38a6e7..d5688d5 100644 ---- a/src/include/access/tupdesc.h -+++ b/src/include/access/tupdesc.h -@@ -75,13 +75,14 @@ typedef struct tupleDesc - Oid tdtypeid; /* composite type ID for tuple type */ - int32 tdtypmod; /* typmod for tuple type */ - bool tdhasoid; /* tuple has oid attribute in its header */ -+ bool tdhassecid; /* tuple has security id in its header */ - int tdrefcount; /* reference count, or -1 if not counting */ - } *TupleDesc; - - --extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid); -+extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid, bool hassecid); - --extern TupleDesc CreateTupleDesc(int natts, bool hasoid, -+extern TupleDesc CreateTupleDesc(int natts, bool hasoid, bool hassecid, - Form_pg_attribute *attrs); - - extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc); -diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h -index 5e989ef..c3ed1d5 100644 ---- a/src/include/bootstrap/bootstrap.h -+++ b/src/include/bootstrap/bootstrap.h -@@ -24,6 +24,7 @@ typedef enum - BgWriterProcess, - WalWriterProcess, - WalReceiverProcess, -+ SecurityWorkerProcess, - - NUM_AUXPROCTYPES /* Must be last! */ - } AuxProcType; -diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h -index 557c311..162ad1c 100644 ---- a/src/include/catalog/heap.h -+++ b/src/include/catalog/heap.h -@@ -61,7 +61,8 @@ extern Oid heap_create_with_catalog(const char *relname, - OnCommitAction oncommit, - Datum reloptions, - bool use_user_acl, -- bool allow_system_table_mods); -+ bool allow_system_table_mods, -+ Oid *secLabels); - - extern void heap_drop_with_catalog(Oid relid); - -@@ -75,13 +76,15 @@ extern List *heap_truncate_find_FKs(List *relationIds); - - extern void InsertPgAttributeTuple(Relation pg_attribute_rel, - Form_pg_attribute new_attribute, -- CatalogIndexState indstate); -+ CatalogIndexState indstate, -+ Oid securityId); - - extern void InsertPgClassTuple(Relation pg_class_desc, - Relation new_rel_desc, - Oid new_rel_oid, - Datum relacl, -- Datum reloptions); -+ Datum reloptions, -+ Oid securityId); - - extern List *AddRelationNewConstraints(Relation rel, - List *newColDefaults, -@@ -106,10 +109,13 @@ extern void RemoveAttrDefaultById(Oid attrdefId); - extern void RemoveStatistics(Oid relid, AttrNumber attnum); - - extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno, -- bool relhasoids); -+ bool relhasoids, bool relhassecids); - - extern Form_pg_attribute SystemAttributeByName(const char *attname, -- bool relhasoids); -+ bool relhasoids, bool relhassecids); -+ -+extern bool SystemAttributeWritable(AttrNumber attno, -+ bool relhasoids, bool relhassecids); - - extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind, - bool allow_system_table_mods); -diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h -index 752a35e..a3da55e 100644 ---- a/src/include/catalog/indexing.h -+++ b/src/include/catalog/indexing.h -@@ -255,6 +255,11 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index, 2703, on pg_type using btree(oid oid_ops - DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); - #define TypeNameNspIndexId 2704 - -+DECLARE_UNIQUE_INDEX(pg_seclabel_secid_index, 3401, on pg_seclabel using btree(secid oid_ops, datid oid_ops, relid oid_ops)); -+#define SecLabelSecidIndexId 3401 -+DECLARE_INDEX(pg_seclabel_label_index, 3402, on pg_seclabel using btree(datid oid_ops, relid oid_ops, label text_ops)); -+#define SecLabelLabelIndexId 3402 -+ - DECLARE_UNIQUE_INDEX(pg_foreign_data_wrapper_oid_index, 112, on pg_foreign_data_wrapper using btree(oid oid_ops)); - #define ForeignDataWrapperOidIndexId 112 - -diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h -index 2296fa2..4a412aa 100644 ---- a/src/include/catalog/pg_class.h -+++ b/src/include/catalog/pg_class.h -@@ -60,6 +60,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO - */ - int2 relchecks; /* # of CHECK constraints for class */ - bool relhasoids; /* T if we generate OIDs for rows of rel */ -+ bool relhassecids; /* T if we generate SIDs for rows of rel */ - bool relhaspkey; /* has (or has had) PRIMARY KEY index */ - bool relhasexclusion; /* has (or has had) exclusion constraint */ - bool relhasrules; /* has (or has had) any rules */ -@@ -93,7 +94,7 @@ typedef FormData_pg_class *Form_pg_class; - * ---------------- - */ - --#define Natts_pg_class 27 -+#define Natts_pg_class 28 - #define Anum_pg_class_relname 1 - #define Anum_pg_class_relnamespace 2 - #define Anum_pg_class_reltype 3 -@@ -113,14 +114,15 @@ typedef FormData_pg_class *Form_pg_class; - #define Anum_pg_class_relnatts 17 - #define Anum_pg_class_relchecks 18 - #define Anum_pg_class_relhasoids 19 --#define Anum_pg_class_relhaspkey 20 --#define Anum_pg_class_relhasexclusion 21 --#define Anum_pg_class_relhasrules 22 --#define Anum_pg_class_relhastriggers 23 --#define Anum_pg_class_relhassubclass 24 --#define Anum_pg_class_relfrozenxid 25 --#define Anum_pg_class_relacl 26 --#define Anum_pg_class_reloptions 27 -+#define Anum_pg_class_relhassecids 20 -+#define Anum_pg_class_relhaspkey 21 -+#define Anum_pg_class_relhasexclusion 22 -+#define Anum_pg_class_relhasrules 23 -+#define Anum_pg_class_relhastriggers 24 -+#define Anum_pg_class_relhassubclass 25 -+#define Anum_pg_class_relfrozenxid 26 -+#define Anum_pg_class_relacl 27 -+#define Anum_pg_class_reloptions 28 - - /* ---------------- - * initial contents of pg_class -@@ -132,13 +134,13 @@ typedef FormData_pg_class *Form_pg_class; - */ - - /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */ --DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ )); -+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t t f f f f f 3 _null_ _null_ )); - DESCR(""); --DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ )); -+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f t f f f f f 3 _null_ _null_ )); - DESCR(""); --DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f f r 25 0 t f f f f f 3 _null_ _null_ )); -+DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f f r 25 0 t t f f f f f 3 _null_ _null_ )); - DESCR(""); --DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f f r 27 0 t f f f f f 3 _null_ _null_ )); -+DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t t f f f f f 3 _null_ _null_ )); - DESCR(""); - - #define RELKIND_INDEX 'i' /* secondary index */ -diff --git a/src/include/catalog/pg_conversion_fn.h b/src/include/catalog/pg_conversion_fn.h -index 3086936..47f49e2 100644 ---- a/src/include/catalog/pg_conversion_fn.h -+++ b/src/include/catalog/pg_conversion_fn.h -@@ -17,7 +17,7 @@ - extern Oid ConversionCreate(const char *conname, Oid connamespace, - Oid conowner, - int32 conforencoding, int32 contoencoding, -- Oid conproc, bool def); -+ Oid conproc, bool def, Oid securityId); - extern void RemoveConversionById(Oid conversionOid); - extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); - -diff --git a/src/include/catalog/pg_largeobject.h b/src/include/catalog/pg_largeobject.h -index 78b3119..cf43db1 100644 ---- a/src/include/catalog/pg_largeobject.h -+++ b/src/include/catalog/pg_largeobject.h -@@ -51,9 +51,10 @@ typedef FormData_pg_largeobject *Form_pg_largeobject; - #define Anum_pg_largeobject_pageno 2 - #define Anum_pg_largeobject_data 3 - --extern Oid LargeObjectCreate(Oid loid); -+extern Oid LargeObjectCreate(Oid loid, Oid securityId); - extern void LargeObjectDrop(Oid loid); - extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId); -+extern void LargeObjectAlterSecLabel(Oid loid, char *new_label); - extern bool LargeObjectExists(Oid loid); - - #endif /* PG_LARGEOBJECT_H */ -diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h -index cc576a5..eeb1079 100644 ---- a/src/include/catalog/pg_namespace.h -+++ b/src/include/catalog/pg_namespace.h -@@ -77,6 +77,6 @@ DESCR("standard public schema"); - /* - * prototypes for functions in pg_namespace.c - */ --extern Oid NamespaceCreate(const char *nspName, Oid ownerId); -+extern Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid secid); - - #endif /* PG_NAMESPACE_H */ -diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h -index 31a9650..254e19b 100644 ---- a/src/include/catalog/pg_proc.h -+++ b/src/include/catalog/pg_proc.h -@@ -3715,6 +3715,10 @@ DESCR("current user privilege on role by role name"); - DATA(insert OID = 2710 ( pg_has_role PGNSP PGUID 12 1 0 0 f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ pg_has_role_id _null_ _null_ _null_ )); - DESCR("current user privilege on role by role oid"); - -+/* SE-PostgreSQL related stuff */ -+DATA(insert OID = 3405 ( 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 = 3407 ( 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 = 1269 ( pg_column_size PGNSP PGUID 12 1 0 0 f f f t f s 1 0 23 "2276" _null_ _null_ _null_ _null_ pg_column_size _null_ _null_ _null_ )); - DESCR("bytes required to store the value, perhaps with compression"); - DATA(insert OID = 2322 ( pg_tablespace_size PGNSP PGUID 12 1 0 0 f f f t f v 1 0 20 "26" _null_ _null_ _null_ _null_ pg_tablespace_size_oid _null_ _null_ _null_ )); -diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h -index 0cb82b0..44ee57b 100644 ---- a/src/include/catalog/pg_proc_fn.h -+++ b/src/include/catalog/pg_proc_fn.h -@@ -37,7 +37,8 @@ extern Oid ProcedureCreate(const char *procedureName, - List *parameterDefaults, - Datum proconfig, - float4 procost, -- float4 prorows); -+ float4 prorows, -+ Oid prosecid); - - extern bool function_parse_error_transpose(const char *prosrc); - -diff --git a/src/include/catalog/pg_seclabel.h b/src/include/catalog/pg_seclabel.h -new file mode 100644 -index 0000000..21b25de ---- /dev/null -+++ b/src/include/catalog/pg_seclabel.h -@@ -0,0 +1,79 @@ -+/* -+ * pg_seclabel.h -+ * Definition of the security label relation (pg_seclabel) -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#ifndef PG_SECLABEL_H -+#define PG_SECLABEL_H -+ -+#include "catalog/genbki.h" -+ -+#include "access/htup.h" -+#include "access/skey.h" -+#include "utils/relcache.h" -+ -+#define SecLabelRelationId 3400 -+ -+CATALOG(pg_seclabel,3400) BKI_SHARED_RELATION BKI_WITHOUT_OIDS -+{ -+ /* Identifier of the security label */ -+ Oid secid; -+ -+ /* OID of the database which referes the entry */ -+ Oid datid; -+ -+ /* OID of the table which refers the entry */ -+ Oid relid; -+ -+ /* Text representation of the security label */ -+ text label; -+} FormData_pg_seclabel; -+ -+/* -+ * Form_pg_seclabel corresponds to a pointer to a tuple with -+ * the format of pg_seclabel relation. -+ */ -+typedef FormData_pg_seclabel *Form_pg_seclabel; -+ -+/* -+ * Compiler constants for pg_seclabel -+ */ -+#define Natts_pg_seclabel 4 -+#define Anum_pg_seclabel_secid 1 -+#define Anum_pg_seclabel_datid 2 -+#define Anum_pg_seclabel_relid 3 -+#define Anum_pg_seclabel_label 4 -+ -+/* -+ * Functions to translate between security label and identifier -+ */ -+extern bool ignore_security_label_input; -+ -+extern bool seclabelCatalogHasSysAttr(Oid relOid); -+extern void seclabelPostBootstrap(void); -+extern void seclabelOnCreateDatabase(Oid src_datOid, Oid dst_datOid); -+extern void seclabelOnDropDatabase(Oid datOid); -+extern void seclabelOnDropTable(Oid relOid); -+ -+extern Oid *seclabelMakeRelationDefaults(TupleDesc tupdesc, List *supOids); -+extern Oid *seclabelMakeToastDefaults(TupleDesc tupdesc, Oid relOid); -+ -+extern Oid seclabelGetNewSecid(Relation rel, HeapTuple tuple); -+ -+extern Oid seclabelRawInput(Oid relOid, char *seclabel); -+extern char *seclabelRawOutput(Oid relOid, Oid secid); -+extern Oid seclabelTransInput(Oid relOid, char *seclabel); -+extern char *seclabelTransOutput(Oid relOid, Oid secid); -+ -+extern Oid seclabelMoveSecid(Oid dst_relid, Oid src_relid, Oid secid); -+extern bool seclabelCompareSecid(Oid relid1, Oid secid1, -+ Oid relid2, Oid secid2); -+ -+extern Datum seclabelSysattOutput(Oid relOid, HeapTuple tuple); -+ -+extern void seclabelRelationReclaim(Oid relOid); -+extern Datum seclabel_to_secid(PG_FUNCTION_ARGS); -+ -+#endif /* PG_SECLABEL_H */ -diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h -index baf3012..c344bf8 100644 ---- a/src/include/catalog/pg_type_fn.h -+++ b/src/include/catalog/pg_type_fn.h -@@ -50,7 +50,8 @@ extern Oid TypeCreate(Oid newTypeOid, - char storage, - int32 typeMod, - int32 typNDims, -- bool typeNotNull); -+ bool typeNotNull, -+ Oid securityId); - - extern void GenerateTypeDependencies(Oid typeNamespace, - Oid typeObjectId, -diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h -index 056fd19..e99dea7 100644 ---- a/src/include/catalog/toasting.h -+++ b/src/include/catalog/toasting.h -@@ -58,5 +58,8 @@ DECLARE_TOAST(pg_shdescription, 2846, 2847); - DECLARE_TOAST(pg_db_role_setting, 2966, 2967); - #define PgDbRoleSettingToastTable 2966 - #define PgDbRoleSettingToastIndex 2967 -+DECLARE_TOAST(pg_seclabel, 3403, 3404); -+#define PgSecLabelToastTable 3403 -+#define PgSecLabelToastIndex 3404 - - #endif /* TOASTING_H */ -diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h -index 83e735a..37b772e 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 f150da3..c085321 100644 ---- a/src/include/commands/dbcommands.h -+++ b/src/include/commands/dbcommands.h -@@ -58,6 +58,7 @@ extern void RenameDatabase(const char *oldname, const char *newname); - extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel); - extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt); - extern void AlterDatabaseOwner(const char *dbname, Oid newOwnerId); -+extern void AlterDatabaseSecLabel(const char *dbname, char *new_label); - - extern Oid get_database_oid(const char *dbname); - extern char *get_database_name(Oid dbid); -diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h -index cd5be6e..9a71ae4 100644 ---- a/src/include/commands/defrem.h -+++ b/src/include/commands/defrem.h -@@ -60,6 +60,8 @@ extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType); - extern void RenameFunction(List *name, List *argtypes, const char *newname); - extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId); - extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId); -+extern void AlterFunctionSecLabel(List *name, List *argtypes, -+ bool isagg, char *new_label); - extern void AlterFunction(AlterFunctionStmt *stmt); - extern void CreateCast(CreateCastStmt *stmt); - extern void DropCast(DropCastStmt *stmt); -diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h -index 62562fa..d122106 100644 ---- a/src/include/commands/schemacmds.h -+++ b/src/include/commands/schemacmds.h -@@ -26,5 +26,6 @@ extern void RemoveSchemaById(Oid schemaOid); - extern void RenameSchema(const char *oldname, const char *newname); - extern void AlterSchemaOwner(const char *name, Oid newOwnerId); - extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId); -+extern void AlterSchemaSecLabel(const char *name, char *new_label); - - #endif /* SCHEMACMDS_H */ -diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h -index c1774a2..cc6b11a 100644 ---- a/src/include/commands/tablecmds.h -+++ b/src/include/commands/tablecmds.h -@@ -35,6 +35,13 @@ extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, - Oid oldNspOid, Oid newNspOid, - bool hasDependEntry); - -+extern void AlterRelationSecLabel(RangeVar *relation, const char *attname, -+ ObjectType objtype, char *new_label); -+extern void AlterRelationSecLabelInternal(Oid relOid, Oid securityId, -+ int expected_parents); -+extern void AlterAttributeSecLabelInternal(Oid relOid, const char *attname, -+ Oid securityId, int expected_parents); -+ - extern void CheckTableNotInUse(Relation rel, const char *stmt); - - extern void ExecuteTruncate(TruncateStmt *stmt); -diff --git a/src/include/commands/tablespace.h b/src/include/commands/tablespace.h -index 3d46eeb..71fa5d2 100644 ---- a/src/include/commands/tablespace.h -+++ b/src/include/commands/tablespace.h -@@ -44,6 +44,7 @@ extern void DropTableSpace(DropTableSpaceStmt *stmt); - extern void RenameTableSpace(const char *oldname, const char *newname); - extern void AlterTableSpaceOwner(const char *name, Oid newOwnerId); - extern void AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt); -+extern void AlterTableSpaceSecLabel(const char *tspaceName, char *newLabel); - - extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo); - -diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h -index 4dc2d4f..e50a83f 100644 ---- a/src/include/commands/typecmds.h -+++ b/src/include/commands/typecmds.h -@@ -43,5 +43,7 @@ extern void AlterTypeNamespace(List *names, const char *newschema); - extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, - bool isImplicitArray, - bool errorOnTableType); -+extern void AlterTypeSecLabel(List *name, char *new_label); -+extern void AlterTypeSecLabelInternal(Oid typeOid, Oid securityId); - - #endif /* TYPECMDS_H */ -diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h -index 13b6aa5..0350a17 100644 ---- a/src/include/executor/executor.h -+++ b/src/include/executor/executor.h -@@ -131,8 +131,8 @@ extern TupleHashEntry FindTupleHashEntry(TupleHashTable hashtable, - /* - * prototypes from functions in execJunk.c - */ --extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, -- TupleTableSlot *slot); -+extern JunkFilter *ExecInitJunkFilter(List *targetList, -+ bool hasoid, bool hassecid, TupleTableSlot *slot); - extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, - TupleDesc cleanTupType, - TupleTableSlot *slot); -@@ -166,6 +166,7 @@ extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, - int instrument_options); - extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); - extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); -+extern bool ExecContextForcesSecids(PlanState *planstate, bool *hassecid); - extern void ExecConstraints(ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, EState *estate); - extern ExecRowMark *ExecFindRowMark(EState *estate, Index rti); -@@ -238,8 +239,8 @@ extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate); - extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); - extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, - TupleDesc tupType); --extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); --extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); -+extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid); -+extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid); - extern TupleDesc ExecTypeFromExprList(List *exprList); - extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); - -diff --git a/src/include/fmgr.h b/src/include/fmgr.h -index e588bb1..1298e3e 100644 ---- a/src/include/fmgr.h -+++ b/src/include/fmgr.h -@@ -51,6 +51,7 @@ typedef struct FmgrInfo - bool fn_retset; /* function returns a set */ - unsigned char fn_stats; /* collect stats if track_functions > this */ - void *fn_extra; /* extra space for use by handler */ -+ char *fn_seclabel; /* function is trusted procedure, or NULL */ - MemoryContext fn_mcxt; /* memory context to store fn_extra in */ - fmNodePtr fn_expr; /* expression parse tree for call, or NULL */ - } FmgrInfo; -diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h -index 401b69e..4e2c59d 100644 ---- a/src/include/nodes/nodes.h -+++ b/src/include/nodes/nodes.h -@@ -346,6 +346,7 @@ typedef enum NodeTag - T_AlterUserMappingStmt, - T_DropUserMappingStmt, - T_AlterTableSpaceOptionsStmt, -+ T_AlterSecLabelStmt, - - /* - * TAGS FOR PARSE TREE NODES (parsenodes.h) -diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h -index 54eebbc..94d0969 100644 ---- a/src/include/nodes/parsenodes.h -+++ b/src/include/nodes/parsenodes.h -@@ -731,6 +731,7 @@ typedef struct RangeTblEntry - Oid checkAsUser; /* if valid, check access as this role */ - Bitmapset *selectedCols; /* columns needing SELECT permission */ - Bitmapset *modifiedCols; /* columns needing INSERT/UPDATE permission */ -+ uint32 rowlvPerms; /* permissions for row-level access controls */ - } RangeTblEntry; - - /* -@@ -1134,6 +1135,8 @@ typedef enum AlterTableType - AT_DropCluster, /* SET WITHOUT CLUSTER */ - AT_AddOids, /* SET WITH OIDS */ - AT_DropOids, /* SET WITHOUT OIDS */ -+ AT_AddSecLabel, /* SET WITH SECURITY LABEL */ -+ AT_DropSecLabel, /* SET WITHOUT SECURITY LABEL */ - AT_SetTableSpace, /* SET TABLESPACE */ - AT_SetRelOptions, /* SET (...) -- AM specific parameters */ - AT_ResetRelOptions, /* RESET (...) -- AM specific parameters */ -@@ -2074,6 +2077,20 @@ typedef struct AlterOwnerStmt - char *newowner; /* the new owner */ - } AlterOwnerStmt; - -+/* ---------------------- -+ * Alter Security Label Statement -+ * ---------------------- -+ */ -+typedef struct AlterSecLabelStmt -+{ -+ NodeTag type; -+ ObjectType objectType; /* OBJECT_TABLE, OBJECT_TYPE, etc */ -+ RangeVar *relation; /* in case it's a table */ -+ List *object; /* in case it's some other object */ -+ List *objarg; /* argument types, if applicable */ -+ char *addname; /* additional name if needed */ -+ Value *secLabel; /* the new security label */ -+} AlterSecLabelStmt; - - /* ---------------------- - * Create Rule Statement -diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h -index ad835d9..71595e3 100644 ---- a/src/include/parser/kwlist.h -+++ b/src/include/parser/kwlist.h -@@ -208,6 +208,7 @@ PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD) - PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD) - PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD) - PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD) -+PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD) - PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD) - PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD) - PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD) -diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in -index fd169b6..4dd3ca7 100644 ---- a/src/include/pg_config.h.in -+++ b/src/include/pg_config.h.in -@@ -418,6 +418,9 @@ - /* Define to 1 if you have the header file. */ - #undef HAVE_SECURITY_PAM_APPL_H - -+/* Define to 1 if you enable SELinux support */ -+#undef HAVE_SELINUX -+ - /* Define to 1 if you have the `setproctitle' function. */ - #undef HAVE_SETPROCTITLE - -diff --git a/src/include/sepgsql/hooks.h b/src/include/sepgsql/hooks.h -new file mode 100644 -index 0000000..b929e6d ---- /dev/null -+++ b/src/include/sepgsql/hooks.h -@@ -0,0 +1,293 @@ -+/* -+ * sepgsql/hooks.h -+ * -+ * Header of SE-PostgreSQL Hooks -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#ifndef SEPGSQL_HOOKS_H -+#define SEPGSQL_HOOKS_H -+ -+#include "executor/tuptable.h" -+#include "nodes/plannodes.h" -+#include "nodes/relation.h" -+#include "storage/fd.h" -+#include "utils/acl.h" -+#include "utils/rel.h" -+#include "utils/snapshot.h" -+ -+/* -+ * misc.c -+ */ -+extern char *sepgsql_get_client_label(void); -+extern char *sepgsql_set_client_label(char *new_label); -+extern void sepgsql_post_bootstraping(void); -+extern void sepgsql_initialize(void); -+extern bool sepgsql_worker_needed(void); -+extern void sepgsql_worker_main(void); -+ -+extern Datum sepgsql_getcon(PG_FUNCTION_ARGS); -+ -+/* -+ * database.c -+ */ -+extern Oid sepgsql_database_create(const char *datName, Oid templateOid); -+extern void sepgsql_database_alter(Oid databaseOid); -+extern Oid sepgsql_database_relabel(Oid databaseOid, char *new_label); -+extern void sepgsql_database_drop(Oid databaseOid, bool cascade); -+extern void sepgsql_database_grant(Oid databaseOid); -+extern void sepgsql_database_comment(Oid databaseOid); -+extern void sepgsql_database_connect(Oid databaseOid); -+extern void sepgsql_database_reindex(Oid databaseOid); -+extern void sepgsql_database_getattr(Oid databaseOid); -+ -+/* -+ * schema.c -+ */ -+extern Oid sepgsql_schema_create(const char *nspName, bool is_temp); -+extern void sepgsql_schema_alter(Oid namespaceOid); -+extern Oid sepgsql_schema_relabel(Oid namespaceOid, char *new_label); -+extern void sepgsql_schema_drop(Oid namespaceOid, bool cascade); -+extern void sepgsql_schema_grant(Oid namespaceOid); -+extern bool sepgsql_schema_search(Oid namespaceOid, bool abort); -+extern void sepgsql_schema_comment(Oid namespaceOid); -+ -+/* -+ * relation.c -+ */ -+extern bool sepgsql_relation_perms(Oid relOid, AclMode aclmask, -+ Bitmapset *selectedCols, -+ Bitmapset *modifiedCols, bool abort); -+extern Oid *sepgsql_relation_create(const char *relName, -+ char relkind, -+ TupleDesc tupDesc, -+ Oid namespaceId, -+ List *supOids, -+ bool createAs); -+extern void sepgsql_relation_alter(Oid relationOid); -+extern void sepgsql_relation_alter_schema(Oid relationOid, Oid newSchema); -+extern void sepgsql_relation_alter_rename(Oid relationOid, -+ const char *newName); -+extern void sepgsql_relation_alter_inherit(Oid childOid, Oid parentOid); -+extern Oid sepgsql_relation_relabel(Oid relationOid, char *new_label); -+extern void sepgsql_relation_drop(Oid relationOid, bool cascade); -+extern void sepgsql_relation_getattr(Oid relationOid); -+extern void sepgsql_relation_grant(Oid relationOid); -+extern void sepgsql_relation_comment(Oid relationOid); -+extern bool sepgsql_relation_cluster(Oid relationOid, bool abort); -+extern void sepgsql_relation_truncate(Relation rel); -+extern void sepgsql_relation_lock(Relation rel); -+extern void sepgsql_relation_reindex(Oid relOid); -+extern void sepgsql_view_replace(Oid viewOid); -+extern void sepgsql_index_create(Oid relationOid, Oid namespaceOid); -+extern void sepgsql_index_reindex(Oid indexOid); -+extern void sepgsql_sequence_get_value(Oid sequenceOid); -+extern void sepgsql_sequence_next_value(Oid sequenceOid); -+extern void sepgsql_sequence_set_value(Oid sequenceOid); -+extern void sepgsql_rule_create(Oid relationOid, -+ const char *ruleName); -+extern void sepgsql_rule_drop(Oid relationOid, -+ const char *ruleName, bool cascade); -+extern void sepgsql_rule_comment(Oid relationOid, const char *ruleName); -+extern void sepgsql_trigger_create(Oid relationOid, const char *triggerName, -+ Oid constrrelid, Oid funcOid); -+extern void sepgsql_trigger_alter(Oid relOid, const char *tgName); -+extern void sepgsql_trigger_drop(Oid relOid, const char *tgName, bool cascade); -+extern void sepgsql_trigger_comment(Oid relOid, const char *tgName); -+extern void sepgsql_constraint_comment(Oid relOid, const char *constName); -+ -+/* -+ * attribute.c -+ */ -+extern Oid sepgsql_attribute_create(Oid relOid, const char *attName); -+extern void sepgsql_attribute_alter(Oid relOid, const char *attName); -+extern Oid sepgsql_attribute_relabel(Oid relOid, const char *attName, -+ char *new_label); -+extern void sepgsql_attribute_drop(Oid relOid, const char *attName, bool cascade); -+extern void sepgsql_attribute_grant(Oid relOid, AttrNumber attnum); -+extern void sepgsql_attribute_comment(Oid relOid, AttrNumber attnum); -+ -+/* -+ * proc.c -+ */ -+extern Oid sepgsql_proc_create(const char *proName, Oid replaced, -+ Oid namespaceOid, Oid langageOid); -+extern void sepgsql_proc_alter(Oid procOid); -+extern void sepgsql_proc_alter_rename(Oid procOid, const char *newName); -+extern void sepgsql_proc_alter_schema(Oid procOid, Oid newSchema); -+extern Oid sepgsql_proc_relabel(Oid procOid, char *new_label); -+extern void sepgsql_proc_drop(Oid procOid, bool cascade); -+extern void sepgsql_proc_grant(Oid procOid); -+extern void sepgsql_proc_comment(Oid procOid); -+extern void sepgsql_proc_execute(Oid procOid); -+extern bool sepgsql_proc_be_inlined(HeapTuple protup); -+extern char *sepgsql_proc_domtrans(HeapTuple protup, MemoryContext mcxt); -+extern Oid sepgsql_aggregate_create(const char *aggName, Oid namespaceId, -+ Oid transFunc, Oid finalFunc); -+extern void sepgsql_aggregate_execute(Oid aggOid); -+ -+/* -+ * type.c -+ */ -+extern Oid sepgsql_type_create(const char *typeName, Oid replaced, -+ Oid namespaceId, char typeType, -+ Oid inputFunc, Oid outputFunc, -+ Oid recvFunc, Oid sendFunc, -+ Oid modinFunc, Oid modoutFunc, -+ Oid analyzeFunc); -+extern void sepgsql_type_alter(Oid typeOid); -+extern void sepgsql_type_alter_rename(Oid typeOid, const char *newName); -+extern void sepgsql_type_alter_schema(Oid typeOid, Oid newSchema); -+extern Oid sepgsql_type_relabel(Oid typeOid, char *newLabel); -+extern void sepgsql_type_drop(Oid typeOid, bool cascade); -+extern void sepgsql_type_comment(Oid typeOid); -+extern Oid sepgsql_cast_create(Oid sourceTypeOid, Oid targetTypeOid, -+ char castMethod, Oid castFuncOid); -+extern void sepgsql_cast_drop(Oid srcTypeOid, Oid dstTypeOid, bool cascade); -+extern void sepgsql_cast_comment(Oid srcTypeOid, Oid dstTypeOid); -+ -+/* -+ * tablespace.h -+ */ -+extern Oid sepgsql_tablespace_create(const char *tablespaceName); -+extern void sepgsql_tablespace_alter(Oid tablespaceOid); -+extern Oid sepgsql_tablespace_relabel(Oid tablespaceOid, char *newLabel); -+extern void sepgsql_tablespace_drop(Oid tablespaceOid, bool cascade); -+extern void sepgsql_tablespace_grant(Oid tablespaceOid); -+extern void sepgsql_tablespace_getattr(Oid tablespaceOid); -+extern void sepgsql_tablespace_comment(Oid tablespaceOid); -+ -+/* -+ * operator.h -+ */ -+extern Oid sepgsql_operator_create(const char *operName, Oid replaced, -+ Oid namespaceId, -+ Oid codeFn, Oid restrictFn, Oid joinFn, -+ Oid commutatorOp, Oid negatorOp); -+extern void sepgsql_operator_alter(Oid operOid); -+extern Oid sepgsql_operator_relabel(Oid operOid, char *newLabel); -+extern void sepgsql_operator_drop(Oid operOid, bool cascade); -+extern void sepgsql_operator_comment(Oid operOid); -+ -+extern Oid sepgsql_opclass_create(const char *opcName, Oid namespaceId, -+ Oid typeOid, Oid opfamilyOid, Oid storageOid); -+extern void sepgsql_opclass_alter(Oid opcOid); -+extern void sepgsql_opclass_alter_rename(Oid opcOid, const char *newName); -+extern void sepgsql_opclass_drop(Oid opcOid, bool cascade); -+extern void sepgsql_opclass_comment(Oid opcOid); -+ -+extern Oid sepgsql_opfamily_create(const char *opfName, -+ Oid namespaceId, Oid amOid); -+extern void sepgsql_opfamily_alter(Oid opfOid, bool isDrop, Oid amOid, -+ List *operators, List *procedures); -+extern void sepgsql_opfamily_alter_rename(Oid opfOid, const char *newName); -+extern void sepgsql_opfamily_alter_owner(Oid opfOid, Oid newOwner); -+extern void sepgsql_opfamily_drop(Oid opfOid, bool cascade); -+extern void sepgsql_opfamily_comment(Oid opfOid); -+ -+/* -+ * role.c -+ */ -+extern Oid sepgsql_role_create(const char *roleName); -+extern void sepgsql_role_alter(Oid roleOid); -+extern Oid sepgsql_role_relabel(Oid roleOid, char *newLabel); -+extern void sepgsql_role_drop(Oid roleOid, bool cascade); -+extern void sepgsql_role_grant(Oid roleOid, bool is_grant, List *memberIds); -+extern void sepgsql_role_comment(Oid roleOid); -+ -+/* -+ * blob.c -+ */ -+extern Oid sepgsql_largeobject_create(Oid loid); -+extern void sepgsql_largeobject_alter(Oid loid); -+extern Oid sepgsql_largeobject_relabel(Oid loid, char *newLabel); -+extern void sepgsql_largeobject_drop(Oid loid, bool cascade); -+extern void sepgsql_largeobject_read(Oid loid, Snapshot snapshot); -+extern void sepgsql_largeobject_write(Oid loid, Snapshot snapshot); -+extern Oid sepgsql_largeobject_import(Oid loid, const char *filename); -+extern void sepgsql_largeobject_export(Oid loid, Snapshot snapshot, -+ const char *filename); -+extern void sepgsql_largeobject_grant(Oid loid); -+extern void sepgsql_largeobject_comment(Oid loid); -+ -+/* -+ * conversion.c -+ */ -+extern Oid sepgsql_conversion_create(const char *convName, -+ Oid namespaceId, Oid conversionFunc); -+extern void sepgsql_conversion_alter(Oid convOid); -+extern void sepgsql_conversion_alter_rename(Oid convOid, const char *newName); -+extern void sepgsql_conversion_drop(Oid convOid, bool cascade); -+extern void sepgsql_conversion_comment(Oid convOid); -+ -+/* -+ * tsearch.c -+ */ -+extern Oid sepgsql_ts_config_create(const char *confName, Oid namespaceId); -+extern void sepgsql_ts_config_alter(Oid confOid); -+extern void sepgsql_ts_config_alter_rename(Oid confOid, const char *newName); -+extern void sepgsql_ts_config_drop(Oid confOid, bool cascade); -+extern void sepgsql_ts_config_comment(Oid confOid); -+extern Oid sepgsql_ts_dict_create(const char *dictName, Oid namespaceId); -+extern void sepgsql_ts_dict_alter(Oid dictOid); -+extern void sepgsql_ts_dict_alter_rename(Oid dictOid, const char *newName); -+extern void sepgsql_ts_dict_drop(Oid dictOid, bool cascade); -+extern void sepgsql_ts_dict_comment(Oid dictOid); -+extern Oid sepgsql_ts_parser_create(const char *parseName, Oid namespaceId, -+ Oid startFunc, Oid tokenFunc, Oid endFunc, -+ Oid headlineFunc, Oid lextypeFunc); -+extern void sepgsql_ts_parser_alter_rename(Oid parseOid, const char *newName); -+extern void sepgsql_ts_parser_drop(Oid parseOid, bool cascade); -+extern void sepgsql_ts_parser_comment(Oid parseOid); -+extern Oid sepgsql_ts_template_create(const char *templateName, -+ Oid namespaceId, -+ Oid initFunc, Oid lexizeFunc); -+extern void sepgsql_ts_template_alter_rename(Oid templateOid, -+ const char *newName); -+extern void sepgsql_ts_template_drop(Oid templateOid, bool cascade); -+extern void sepgsql_ts_template_comment(Oid templateOid); -+ -+/* -+ * fdw.c -+ */ -+extern Oid sepgsql_fdw_create(const char *fdwName, Oid validatorFunc); -+extern void sepgsql_fdw_alter(Oid fdwOid, Oid newValidator); -+extern void sepgsql_fdw_drop(Oid fdwOid, bool cascade); -+extern void sepgsql_fdw_grant(Oid fdwOid); -+ -+extern Oid sepgsql_fserver_create(const char *fservName, Oid fdwOid); -+extern void sepgsql_fserver_alter(Oid fservOid); -+extern void sepgsql_fserver_drop(Oid fservOid, bool cascade); -+extern void sepgsql_fserver_grant(Oid fservOid); -+ -+extern Oid sepgsql_user_mapping_create(Oid mappedRoleId, Oid fservOid); -+extern void sepgsql_user_mapping_alter(Oid umapOid); -+extern void sepgsql_user_mapping_drop(Oid umapOid, bool cascade); -+ -+extern void sepgsql_file_getattr(const char *filename); -+extern void sepgsql_file_read(const char *filename); -+extern void sepgsql_file_write(const char *filename, bool may_create); -+extern void sepgsql_file_append(const char *filename); -+extern void sepgsql_file_unlink(const char *filename); -+extern void sepgsql_file_rename(const char *oldpath, const char *newpath); -+extern void sepgsql_dir_list(const char *dirname); -+ -+/* -+ * row-level access controls -+ */ -+#define SEPGSQL_ROWLV_FILTER 1 -+#define SEPGSQL_ROWLV_ABORT 2 -+#define SEPGSQL_ROWLV_BYPASS 3 -+ -+extern int sepgsql_rowlv_get_mode(void); -+extern int sepgsql_rowlv_set_mode(int new_mode); -+extern bool sepgsql_rowlv_quals(Relation rel, uint32 required, -+ TupleTableSlot *slot, bool abort); -+extern bool sepgsql_rowlv_copyto(Relation rel, HeapTuple tuple); -+extern uint32 sepgsql_rowlv_permissions(RangeTblEntry *rte); -+ -+extern void sepgsql_tuple_insert(Relation rel, HeapTuple tuple); -+extern void sepgsql_tuple_update(Relation rel, ItemPointer otid, HeapTuple newtup); -+ -+#endif /* SEPGSQL_HOOKS_H */ -diff --git a/src/include/sepgsql/sepgsql.h b/src/include/sepgsql/sepgsql.h -new file mode 100644 -index 0000000..5fd7195 ---- /dev/null -+++ b/src/include/sepgsql/sepgsql.h -@@ -0,0 +1,318 @@ -+/* -+ * sepgsql/sepgsql.h -+ * -+ * Header of SE-PostgreSQL Internal -+ * -+ * Copyright (C) 2006-2010, NEC Corporation -+ * KaiGai Kohei -+ */ -+#ifndef SEPGSQL_H -+#define SEPGSQL_H -+ -+#include "utils/snapshot.h" -+ -+/* GUC : sepostgresql */ -+extern int sepostgresql_mode; -+ -+#define SEPGSQL_MODE_DEFAULT 1 -+#define SEPGSQL_MODE_ENFORCING 2 -+#define SEPGSQL_MODE_PERMISSIVE 3 -+#define SEPGSQL_MODE_INTERNAL 4 -+#define SEPGSQL_MODE_DISABLED 5 -+ -+/* GUC: sepostgresql_mcstrans */ -+extern bool sepgsql_mcstrans; -+ -+/* GUC: sepostgresql_debug_audit */ -+extern bool sepgsql_debug_audit; -+ -+/* Objject classes and permissions internally used */ -+enum SepgsqlClasses -+{ -+ SEPG_CLASS_PROCESS = 0, -+ SEPG_CLASS_FILE, -+ SEPG_CLASS_DIR, -+ SEPG_CLASS_LNK_FILE, -+ SEPG_CLASS_CHR_FILE, -+ SEPG_CLASS_BLK_FILE, -+ SEPG_CLASS_SOCK_FILE, -+ SEPG_CLASS_FIFO_FILE, -+ SEPG_CLASS_DB_DATABASE, -+ SEPG_CLASS_DB_SCHEMA, -+ SEPG_CLASS_DB_TABLE, -+ SEPG_CLASS_DB_VIEW, -+ SEPG_CLASS_DB_SEQUENCE, -+ SEPG_CLASS_DB_PROCEDURE, -+ SEPG_CLASS_DB_COLUMN, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_CLASS_DB_LANGUAGE, -+ SEPG_CLASS_MAX, -+}; -+ -+#define SEPG_PROCESS__TRANSITION (1<<0) -+ -+#define SEPG_FILE__READ (1<<0) -+#define SEPG_FILE__WRITE (1<<1) -+#define SEPG_FILE__CREATE (1<<2) -+#define SEPG_FILE__GETATTR (1<<3) -+#define SEPG_FILE__UNLINK (1<<4) -+#define SEPG_FILE__RENAME (1<<5) -+#define SEPG_FILE__APPEND (1<<6) -+ -+#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_DIR__UNLINK (SEPG_FILE__UNLINK) -+#define SEPG_DIR__RENAME (SEPG_FILE__RENAME) -+#define SEPG_DIR__SEARCH (1<<6) -+#define SEPG_DIR__ADD_NAME (1<<7) -+#define SEPG_DIR__REMOVE_NAME (1<<8) -+#define SEPG_DIR__RMDIR (1<<9) -+#define SEPG_DIR__REPARENT (1<<10) -+ -+#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_LNK_FILE__UNLINK (SEPG_FILE__UNLINK) -+#define SEPG_LNK_FILE__RENAME (SEPG_FILE__RENAME) -+ -+#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_CHR_FILE__UNLINK (SEPG_FILE__UNLINK) -+#define SEPG_CHR_FILE__RENAME (SEPG_FILE__RENAME) -+ -+#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_BLK_FILE__UNLINK (SEPG_FILE__UNLINK) -+#define SEPG_BLK_FILE__RENAME (SEPG_FILE__RENAME) -+ -+#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_SOCK_FILE__UNLINK (SEPG_FILE__UNLINK) -+#define SEPG_SOCK_FILE__RENAME (SEPG_FILE__RENAME) -+ -+#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_FIFO_FILE__UNLINK (SEPG_FILE__UNLINK) -+#define SEPG_FIFO_FILE__RENAME (SEPG_FILE__RENAME) -+ -+#define SEPG_DB_DATABASE__CREATE (1<<0) -+#define SEPG_DB_DATABASE__DROP (1<<1) -+#define SEPG_DB_DATABASE__GETATTR (1<<2) -+#define SEPG_DB_DATABASE__SETATTR (1<<3) -+#define SEPG_DB_DATABASE__RELABELFROM (1<<4) -+#define SEPG_DB_DATABASE__RELABELTO (1<<5) -+#define SEPG_DB_DATABASE__ACCESS (1<<6) -+#define SEPG_DB_DATABASE__LOAD_MODULE (1<<7) -+ -+#define SEPG_DB_SCHEMA__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_SCHEMA__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_SCHEMA__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_SCHEMA__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_SCHEMA__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_SCHEMA__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_SCHEMA__SEARCH (1<<6) -+#define SEPG_DB_SCHEMA__ADD_NAME (1<<7) -+#define SEPG_DB_SCHEMA__REMOVE_NAME (1<<8) -+ -+#define SEPG_DB_TABLE__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_TABLE__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_TABLE__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_TABLE__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_TABLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_TABLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_TABLE__SELECT (1<<6) -+#define SEPG_DB_TABLE__UPDATE (1<<7) -+#define SEPG_DB_TABLE__INSERT (1<<8) -+#define SEPG_DB_TABLE__DELETE (1<<9) -+#define SEPG_DB_TABLE__LOCK (1<<10) -+#define SEPG_DB_TABLE__INDEXON (1<<11) -+ -+#define SEPG_DB_SEQUENCE__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_SEQUENCE__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_SEQUENCE__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_SEQUENCE__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_SEQUENCE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_SEQUENCE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_SEQUENCE__GET_VALUE (1<<6) -+#define SEPG_DB_SEQUENCE__NEXT_VALUE (1<<7) -+#define SEPG_DB_SEQUENCE__SET_VALUE (1<<8) -+ -+#define SEPG_DB_VIEW__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_VIEW__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_VIEW__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_VIEW__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_VIEW__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_VIEW__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_VIEW__EXPAND (1<<6) -+ -+#define SEPG_DB_PROCEDURE__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_PROCEDURE__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_PROCEDURE__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_PROCEDURE__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_PROCEDURE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_PROCEDURE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_PROCEDURE__EXECUTE (1<<6) -+#define SEPG_DB_PROCEDURE__ENTRYPOINT (1<<7) -+#define SEPG_DB_PROCEDURE__INSTALL (1<<8) -+ -+#define SEPG_DB_COLUMN__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_COLUMN__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_COLUMN__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_COLUMN__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_COLUMN__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_COLUMN__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_COLUMN__SELECT (1<<6) -+#define SEPG_DB_COLUMN__UPDATE (1<<7) -+#define SEPG_DB_COLUMN__INSERT (1<<8) -+ -+#define SEPG_DB_TUPLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_TUPLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_TUPLE__SELECT (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_TUPLE__UPDATE (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_TUPLE__INSERT (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_TUPLE__DELETE (SEPG_DB_DATABASE__DROP) -+ -+#define SEPG_DB_BLOB__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_BLOB__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_BLOB__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_BLOB__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_BLOB__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_BLOB__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_BLOB__READ (1<<6) -+#define SEPG_DB_BLOB__WRITE (1<<7) -+#define SEPG_DB_BLOB__IMPORT (1<<8) -+#define SEPG_DB_BLOB__EXPORT (1<<9) -+ -+#define SEPG_DB_LANGUAGE__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_LANGUAGE__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_LANGUAGE__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_LANGUAGE__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_LANGUAGE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_LANGUAGE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_LANGUAGE__IMPLEMENTE (1<<6) -+#define SEPG_DB_LANGUAGE__EXECUTE (1<<7) -+ -+/* -+ * sepgsql_sid_t : alternative representation of security context -+ */ -+typedef struct { -+ Oid relid; -+ Oid secid; -+} sepgsql_sid_t; -+ -+struct av_decision; -+ -+/* -+ * selinux.c -+ */ -+extern bool sepgsql_is_enabled(void); -+extern bool sepgsql_get_enforce(void); -+extern const char *sepgsql_show_mode(void); -+ -+extern Size sepgsql_shmem_size(void); -+ -+extern void sepgsql_audit_log(bool denied, -+ char *scontext, -+ char *tcontext, -+ uint16 tclass, -+ uint32 audited, -+ const char *audit_name); -+extern void sepgsql_compute_avd(char *scontext, -+ char *tcontext, -+ uint16 tclass, -+ struct av_decision *avd); -+extern bool sepgsql_compute_perms(char *scontext, -+ char *tcontext, -+ uint16 tclass, -+ uint32 required, -+ const char *audit_name, -+ bool abort); -+extern char *sepgsql_compute_create(char *scontext, -+ char *tcontext, -+ uint16 tclass); -+extern bool sepgsql_client_perms(sepgsql_sid_t tsid, -+ uint16 tclass, -+ uint32 required, -+ const char *audit_name, -+ bool abort); -+extern sepgsql_sid_t sepgsql_client_create_secid(sepgsql_sid_t tsid, -+ uint16 tclass, -+ Oid nrelid); -+extern char *sepgsql_client_create_label(sepgsql_sid_t tsid, -+ uint16 tclass); -+extern void sepgsql_avc_worker_main(void); -+ -+/* -+ * avc.c -+ */ -+extern Size sepgsql_shmem_size(void); -+extern void sepgsql_avc_init(void); -+extern void sepgsql_avc_switch(const char *scontext); -+ -+ -+/* -+ * label.c -+ */ -+extern sepgsql_sid_t sepgsql_move_secid(Oid dst_relid, sepgsql_sid_t ssid); -+ -+extern sepgsql_sid_t sepgsql_get_default_database_secid(Oid templateOid); -+extern sepgsql_sid_t sepgsql_get_default_schema_secid(Oid databaseOid); -+extern sepgsql_sid_t sepgsql_get_default_table_secid(Oid namespaceOid); -+extern sepgsql_sid_t sepgsql_get_default_sequence_secid(Oid namespaceOid); -+extern sepgsql_sid_t sepgsql_get_default_view_secid(Oid namespaceOid); -+extern sepgsql_sid_t sepgsql_get_default_proc_secid(Oid namespaceOid); -+extern sepgsql_sid_t sepgsql_get_default_column_secid(Oid tableOid); -+extern sepgsql_sid_t sepgsql_get_default_tuple_secid(Oid tableOid); -+extern sepgsql_sid_t sepgsql_get_default_blob_secid(Oid databaseOid); -+extern Oid sepgsql_get_default_secid(Relation rel, HeapTuple tuple); -+ -+extern void sepgsql_initial_labeling(void); -+ -+extern char *sepgsql_mcstrans_out(char *label); -+extern char *sepgsql_mcstrans_in(char *label); -+extern char *sepgsql_rawlabel_out(char *label); -+extern char *sepgsql_rawlabel_in(char *label); -+ -+/* -+ * sepgsql_(object)_common -+ */ -+extern bool sepgsql_database_common(Oid datOid, uint32 required, bool abort); -+extern bool sepgsql_schema_common(Oid nspOid, uint32 required, bool abort); -+extern bool sepgsql_relation_common(Oid relOid, uint32 required, bool abort); -+extern bool sepgsql_attribute_common(Oid relOid, AttrNumber attno, -+ uint32 required, bool abort); -+extern bool sepgsql_proc_common(Oid procOid, uint32 required, bool abort); -+extern bool sepgsql_type_common(Oid typeOid, uint32 required, bool abort); -+extern bool sepgsql_cast_common(Oid srcTypeOid, Oid dstTypeOid, -+ uint32 required, bool abort); -+extern bool sepgsql_tablespace_common(Oid tspaceOid, uint32 required, bool abort); -+extern bool sepgsql_operator_common(Oid operOid, uint32 required, bool abort); -+extern bool sepgsql_opclass_common(Oid opcOid, uint32 required, bool abort); -+extern bool sepgsql_opfamily_common(Oid opfOid, uint32 required, bool abort); -+extern bool sepgsql_role_common(Oid roleOid, uint32 required, bool abort); -+extern bool sepgsql_largeobejct_common(Oid loid, Snapshot snapshot, -+ uint32 required, bool abort); -+extern bool sepgsql_conversion_common(Oid convOid, uint32 required, bool abort); -+extern bool sepgsql_largeobject_common(Oid loid, Snapshot snapshot, -+ uint32 required, bool abort); -+extern bool sepgsql_ts_config_common(Oid confOid, uint32 required, bool abort); -+extern bool sepgsql_ts_dict_common(Oid dictOid, uint32 required, bool abort); -+extern bool sepgsql_ts_parser_common(Oid parseOid, uint32 required, bool abort); -+extern bool sepgsql_ts_template_common(Oid templateOid, uint32 required, bool abort); -+extern bool sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort); -+extern bool sepgsql_fserver_common(Oid fservOid, uint32 required, bool abort); -+extern bool sepgsql_user_mapping_common(Oid umapOid, uint32 required, bool abort); -+ -+#endif /* SEPGSQL_H */ -diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h -index b8de372..2bd6973 100644 ---- a/src/include/storage/large_object.h -+++ b/src/include/storage/large_object.h -@@ -70,7 +70,7 @@ typedef struct LargeObjectDesc - - /* inversion stuff in inv_api.c */ - extern void close_lo_relation(bool isCommit); --extern Oid inv_create(Oid lobjId); -+extern Oid inv_create(Oid lobjId, Oid securityId); - extern LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt); - extern void inv_close(LargeObjectDesc *obj_desc); - extern int inv_drop(Oid lobjId); -diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h -index 0322007..f807707 100644 ---- a/src/include/storage/lwlock.h -+++ b/src/include/storage/lwlock.h -@@ -70,6 +70,7 @@ typedef enum LWLockId - RelationMappingLock, - AsyncCtlLock, - AsyncQueueLock, -+ SepgsqlAvcLock, - /* Individual lock IDs end here */ - FirstBufMappingLock, - FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, -diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h -index 9eb37b88..6165c4d 100644 ---- a/src/include/utils/guc.h -+++ b/src/include/utils/guc.h -@@ -166,6 +166,7 @@ extern bool log_btree_build_stats; - - extern PGDLLIMPORT bool check_function_bodies; - extern bool default_with_oids; -+extern bool default_with_secids; - extern bool SQL_inheritance; - - extern int log_min_error_statement; -diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h -index 9faefbe..90b7c94 100644 ---- a/src/include/utils/syscache.h -+++ b/src/include/utils/syscache.h -@@ -101,6 +101,8 @@ extern bool SearchSysCacheExists(int cacheId, - Datum key1, Datum key2, Datum key3, Datum key4); - extern Oid GetSysCacheOid(int cacheId, - Datum key1, Datum key2, Datum key3, Datum key4); -+extern Oid GetSysCacheSecid(int cacheId, -+ Datum key1, Datum key2, Datum key3, Datum key4); - - extern HeapTuple SearchSysCacheAttName(Oid relid, const char *attname); - extern HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname); -@@ -154,6 +156,15 @@ extern struct catclist *SearchSysCacheList(int cacheId, int nkeys, - #define GetSysCacheOid4(cacheId, key1, key2, key3, key4) \ - GetSysCacheOid(cacheId, key1, key2, key3, key4) - -+#define GetSysCacheSecid1(cacheId, key1) \ -+ GetSysCacheSecid(cacheId, key1, 0, 0, 0) -+#define GetSysCacheSecid2(cacheId, key1, key2) \ -+ GetSysCacheSecid(cacheId, key1, key2, 0, 0) -+#define GetSysCacheSecid3(cacheId, key1, key2, key3) \ -+ GetSysCacheSecid(cacheId, key1, key2, key3, 0) -+#define GetSysCacheSecid4(cacheId, key1, key2, key3, key4) \ -+ GetSysCacheSecid(cacheId, key1, key2, key3, key4) -+ - #define SearchSysCacheList1(cacheId, key1) \ - SearchSysCacheList(cacheId, 1, key1, 0, 0, 0) - #define SearchSysCacheList2(cacheId, key1, key2) \ -diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c -index 589d514..5370346 100644 ---- a/src/pl/plpgsql/src/pl_comp.c -+++ b/src/pl/plpgsql/src/pl_comp.c -@@ -1965,7 +1965,7 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars) - - row = palloc0(sizeof(PLpgSQL_row)); - row->dtype = PLPGSQL_DTYPE_ROW; -- row->rowtupdesc = CreateTemplateTupleDesc(numvars, false); -+ row->rowtupdesc = CreateTemplateTupleDesc(numvars, false, false); - row->nfields = numvars; - row->fieldnames = palloc(numvars * sizeof(char *)); - row->varnos = palloc(numvars * sizeof(int)); -diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out -index 1d9e110..9596b0b 100644 ---- a/src/test/regress/expected/sanity_check.out -+++ b/src/test/regress/expected/sanity_check.out -@@ -114,6 +114,7 @@ SELECT relname, relhasindex - pg_pltemplate | t - pg_proc | t - pg_rewrite | t -+ pg_seclabel | t - pg_shdepend | t - pg_shdescription | t - pg_statistic | t -@@ -153,7 +154,7 @@ SELECT relname, relhasindex - timetz_tbl | f - tinterval_tbl | f - varchar_tbl | f --(142 rows) -+(143 rows) - - -- - -- another sanity check: every system catalog that has OIDs should have diff --git a/sepostgresql-fedora-prefix.patch b/sepostgresql-fedora-prefix.patch deleted file mode 100644 index 98cd3b2..0000000 --- a/sepostgresql-fedora-prefix.patch +++ /dev/null @@ -1,105 +0,0 @@ -diff --git a/src/Makefile.global.in b/src/Makefile.global.in -index 0e3bed5..aee6064 100644 ---- a/src/Makefile.global.in -+++ b/src/Makefile.global.in -@@ -74,14 +74,14 @@ bindir := @bindir@ - datadir := @datadir@ - ifeq "$(findstring pgsql, $(datadir))" "" - ifeq "$(findstring postgres, $(datadir))" "" --override datadir := $(datadir)/postgresql -+override datadir := $(datadir)/sepgsql - endif - endif - - sysconfdir := @sysconfdir@ - ifeq "$(findstring pgsql, $(sysconfdir))" "" - ifeq "$(findstring postgres, $(sysconfdir))" "" --override sysconfdir := $(sysconfdir)/postgresql -+override sysconfdir := $(sysconfdir)/sepgsql - endif - endif - -@@ -90,7 +90,7 @@ libdir := @libdir@ - pkglibdir = $(libdir) - ifeq "$(findstring pgsql, $(pkglibdir))" "" - ifeq "$(findstring postgres, $(pkglibdir))" "" --override pkglibdir := $(pkglibdir)/postgresql -+override pkglibdir := $(pkglibdir)/sepgsql - endif - endif - -@@ -99,7 +99,7 @@ includedir := @includedir@ - pkgincludedir = $(includedir) - ifeq "$(findstring pgsql, $(pkgincludedir))" "" - ifeq "$(findstring postgres, $(pkgincludedir))" "" --override pkgincludedir := $(pkgincludedir)/postgresql -+override pkgincludedir := $(pkgincludedir)/sepgsql - endif - endif - -@@ -108,7 +108,7 @@ mandir := @mandir@ - docdir := @docdir@ - ifeq "$(findstring pgsql, $(docdir))" "" - ifeq "$(findstring postgres, $(docdir))" "" --override docdir := $(docdir)/postgresql -+override docdir := $(docdir)/sepgsql - endif - endif - -diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c -index 497bdf0..54908f8 100644 ---- a/src/bin/initdb/initdb.c -+++ b/src/bin/initdb/initdb.c -@@ -2722,7 +2722,7 @@ main(int argc, char *argv[]) - */ - putenv("TZ=GMT"); - -- if ((ret = find_other_exec(argv[0], "postgres", PG_BACKEND_VERSIONSTR, -+ if ((ret = find_other_exec(argv[0], "sepostgres", PG_BACKEND_VERSIONSTR, - backend_exec)) < 0) - { - char full_path[MAXPGPATH]; -diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c -index 814ce97..2550e57 100644 ---- a/src/bin/pg_ctl/pg_ctl.c -+++ b/src/bin/pg_ctl/pg_ctl.c -@@ -654,7 +654,7 @@ do_init(void) - char cmd[MAXPGPATH]; - - if (exec_path == NULL) -- exec_path = find_other_exec_or_die(argv0, "initdb", "initdb (PostgreSQL) " PG_VERSION "\n"); -+ exec_path = find_other_exec_or_die(argv0, "initdb.sepgsql", "initdb (PostgreSQL) " PG_VERSION "\n"); - - if (pgdata_opt == NULL) - pgdata_opt = ""; -@@ -699,7 +699,7 @@ do_start(void) - pgdata_opt = ""; - - if (exec_path == NULL) -- exec_path = find_other_exec_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR); -+ exec_path = find_other_exec_or_die(argv0, "sepostgres", PG_BACKEND_VERSIONSTR); - - #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE) - if (allow_core_files) -@@ -1069,7 +1069,7 @@ pgwin32_CommandLine(bool registration) - } - else - { -- ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR, -+ ret = find_other_exec(argv0, "sepostgres", PG_BACKEND_VERSIONSTR, - cmdLine); - if (ret != 0) - { -diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c -index 83f1678..a0a9444 100644 ---- a/src/bin/pg_dump/pg_dumpall.c -+++ b/src/bin/pg_dump/pg_dumpall.c -@@ -156,7 +156,7 @@ main(int argc, char *argv[]) - } - } - -- if ((ret = find_other_exec(argv[0], "pg_dump", PGDUMP_VERSIONSTR, -+ if ((ret = find_other_exec(argv[0], "sepg_dump", PGDUMP_VERSIONSTR, - pg_dump_bin)) < 0) - { - char full_path[MAXPGPATH]; diff --git a/sepostgresql.8 b/sepostgresql.8 deleted file mode 100644 index 9c60ef5..0000000 --- a/sepostgresql.8 +++ /dev/null @@ -1,137 +0,0 @@ -.TH "sepostgresql" "8" "Jul 15 2007" "kaigai@kaigai.gr.jp" "Security-Enhanced PostgreSQL" - -.SH "NAME" -sepostgresql \- Security-Enhances PostgreSQL - -.SH "DESCRIPTION" -Security-Enhanced PostgreSQL (SE-PostgreSQL) is an enhancement of PostgreSQL, -to apply fine grained mandatory access control for database objects based on -the security policy of SELinux. -These features enable to apply flexible integrated access control policy -on both of operating system and database management system, during all -stages of the life of the information. -.PP -This document describes the way to customize SE-PostgreSQL on the default -security policy. - -.SH "BOOLEANS" -The SELinux policy is customizable via BOOLEAN variable. This variable has -two states, 1 (on) or 0 (off). We can validate or invalidate a part of the -security policy depending on the state of boolean variables. - -\fBsepgsql_enable_users_ddl\fP enables to toggle permissions of confined -users/applications to invoke DDL statement, like CREATE TABLE. It is set to -\fBon\fP in the default. -In most cases, DDL statements are used to set up initial database structure, -and permissions to invoke them are not necessary on operation phase. -You can turn off this boolean as follows: - -.EX -setsebool -P sepgsql_enable_users_ddl ( \fBon\fP | off ) -.EE - -Rest of booleans are provided by \fBselinux-devel.pp\fP policy module. -It provides developments/debugs related permissions. -You can install it as follows: - -.EX -semodule -i /usr/share/selinux/targeted/sepostgresql-devel.pp -.EE - -\fBsepgsql_enable_auditallow\fP toggles output of audit messages in the case -when required permission checks are allowed, except for tuples because it -easily make a flood of audit logs. -In the default, it is set to off. You can set it as follows: - -.EX -setsebool -P sepgsql_enable_auditallow ( on | \fBoff\fP ) -.EE - -\fBsepgsql_enable_auditdeny\fP toggles output of audit messages in the case -when required permission checks are denied, except for tuples because it -easily make a flood of audit logs. -In the default, it is set to on. You can set it as follows: - -.EX -setsebool -P sepgsql_enable_auditdeny ( \fBon\fP | off ) -.EE - -\fBsepgsql_regression_test_mode\fP allows to load shared libraries deployed -on user's home directory. We recommend you to keep \fBoff\fP in operation -phase to prevent to load malicious libraries. -However, typical PostgreSQL regression test requires to load it, so we -have to reduce several restriction during the test. -In the default, it is set to off. You can set it as follows: - -.EX -setsebool -P sepgsql_regression_test_mode ( on | \fBoff\fP ) -.EE - -.SH "TYPES" - -\fBsepgsql_db_t\fP is a only type for database itself. -It is attched for newly created databases in the default. - -\fBsepgsql_table_t\fP is a type for tables, columns and tuples. -It is the default type of newly created tables by unconfined or -non-roled domain. It allows confined clietns to access with any -kind of operations except for relabeling, so we can use this type -for compatible purpose. - -\fBsepgsql_secret_table_t\fP is a type for tables, columns and tuples. -It never allows confined clients to access, so we can use this type -to store sensitive information. We reccomend to apply trusted procedures -to access tables/columns/tuples with this type under safe operation. - -\fBsepgsql_ro_table_t\fP is a type for read-only tables, columns and tuples. -It does not allow confined clients to modify any objects with this type. - -\fBsepgsql_fixed_table_t\fP is a type for non-manupulatable tables, columns -and tuples. It does not allow confined clients to update or delete any -objects with this type. - -\fBsepgsql_ROLE_table_t\fP is a type for a role specific tables, columns -and tuples. It allows confined clients with its role to access with any -kind of operations except for relabeling. -It is the default type of newly created tables by confined clients with -its role, and we can use this type to describe role level separation. - -\fBsepgsql_proc_t\fP is a type for procedures. -It is attached for newly created procedures by unconfined clients. -It allows any clients to invoke procedures with this type. -All of PostgreSQL built-in functions are labeled as this type in the default. - -\fBsepgsql_ROLE_proc_t\fP is a type for a role specific procedure. -It is attached for newly created procedures by confined clients with its role. -It allows clients with same role to invoke procedure with this type. -Note that unconfined clients cannot invoke this type to avoid to execute -dangerous functions with unconfined authorities. They have to confirm its -contains and relabel to \fBsepgsql_proc_t\fP for its invocation. - -\fBsepgsql_trusted_proc_exec_t\fP is a type for trusted procedures. -To call procedures with this type invokes domain transition to -unconfined domain, so it can access any kind of database objects. -We can use this type to provide a secure method to access sensitive -information. - -\fBsepgsql_blob_t\fP is a type for binary large objects (blob). -It is attached for newly created blob in the default. -Non-administrative clients can read and write the blobs with this type. - -\fBsepgsql_ro_blob_t\fP is a type for read-only binary large objects (blob). -Non-administrative clients cannot write the blobs with this type. - -.SH "BACKUP and RESTORE" -\fI--enable-selinux\fP option in \fBsepg_dump\fP and \fBsepg_dumpall\fP enable to dump database image with security context. We can restore the dumped image using the standard \fIpg_restore\fP and so on. - -.EX -Example) -$ sepg_dump -Ft -b --enable-selinux postgres | gzip -c > postgres.tgz -.EE - -.SH AUTHOR -This manual page was written by KaiGai Kohei - -.SH "SEE ALSO" - -selinux(8), boolean(8) diff --git a/sepostgresql.init b/sepostgresql.init deleted file mode 100644 index bc668c4..0000000 --- a/sepostgresql.init +++ /dev/null @@ -1,205 +0,0 @@ -#!/bin/sh -# sepostgresql This is the init script for starting up SE-PostgreSQL -# -# chkconfig: - 62 38 -# description: Starts and stops the SE-PostgreSQL backend daemon -# processname: postmaster -# pidfile: /var/run/postmaster.pid -#--------------------------------------------------------------------- - -# source function library -. /etc/rc.d/init.d/functions - -# get config -. /etc/sysconfig/network - -# find the name of the script -NAME=`basename $0` -if [ ${NAME:0:1} = "S" -o ${NAME:0:1} = "K" ]; then - NAME=${NAME:3} -fi - -PGVERSION=`rpm -q --queryformat='%{version}' ${NAME}` -PGMAJORVERSION=`echo "$PGVERSION" | sed 's/^\([0-9]*\.[0-9a-z]*\).*$/\1/'` - -# set defaults for configurable variables -SEPGSQL_CTL="/usr/bin/sepg_ctl" -SEPGSQL_DATA="/var/lib/sepgsql/data" -SEPGSQL_OPTS="-i -p 5432" -SEPGSQL_STARTUP_LOG="/var/lib/sepgsql/pgstartup.log" -SEPGSQL_LOG="/var/log/sepostgresql.log" - -# override defaults from /etc/sysconfig/sepostgresql -test -f /etc/sysconfig/${NAME} && . /etc/sysconfig/${NAME} - -# Check that networking is up. -test "${NETWORKING}" = "no" && exit 0 -test -f "/usr/bin/sepostgres" || exit 1 - -script_result=0 - -do_start() { - PSQL_START=$"Starting ${NAME} service: " - echo -n "$PSQL_START" - - # make sure startup-time log file is valid - if [ ! -e "${SEPGSQL_STARTUP_LOG}" -a ! -h "${SEPGSQL_STARTUP_LOG}" ]; then - touch "${SEPGSQL_STARTUP_LOG}" || exit 1 - chown sepgsql:sepgsql "${SEPGSQL_STARTUP_LOG}" - chmod 600 "${SEPGSQL_STARTUP_LOG}" - /sbin/restorecon "${SEPGSQL_STARTUP_LOG}" - fi - - # check for the SEPGSQL_DATA structure - if [ -f "${SEPGSQL_DATA}/PG_VERSION" ] && [ -d "${SEPGSQL_DATA}/base" ]; then - if [ x`cat "${SEPGSQL_DATA}/PG_VERSION"` != x"${PGMAJORVERSION}" ]; then - echo_failure - echo - echo "HINT: An old version of the database format was found." - echo "HINT: You need to upgrade the data format before using SE-PostgreSQL." - exit 1 - fi - else - echo_failure - echo - echo "HINT: ${SEPGSQL_DATA} is missing." - echo "HINT: Use '/etc/init.d/${NAME} initdb'" - echo "HINT: to initialize the database cluster first." - exit 1 - fi - - # make sure SEPGSQL_LOG - touch ${SEPGSQL_LOG} - chown sepgsql:sepgsql ${SEPGSQL_LOG} - chmod 600 ${SEPGSQL_LOG} - test -x /sbin/restorecon && /sbin/restorecon ${SEPGSQL_LOG} - - /sbin/runuser sepgsql -c "${SEPGSQL_CTL} -w -t 10 -l ${SEPGSQL_LOG} -D ${SEPGSQL_DATA} -o '${SEPGSQL_OPTS}' start" \ - >> ${SEPGSQL_STARTUP_LOG} 2>&1 < /dev/null - sleep 1 - PID=`/sbin/runuser sepgsql -c "${SEPGSQL_CTL} -D ${SEPGSQL_DATA} status 2>/dev/null \ - | sed 's/^.*PID: //g' | sed 's/[^0-9].*$//g'"` - if [ ${PIPESTATUS[0]} -eq 0 ]; then - echo "$PID" > "/var/run/${NAME}.pid" - touch "/var/lock/subsys/${NAME}.lock" - echo_success - else - script_result=1 - echo_failure - fi - echo -} - -do_stop() { - echo -n $"Stopping ${NAME} service: " - /sbin/runuser sepgsql -c "${SEPGSQL_CTL} -D ${SEPGSQL_DATA} stop" \ - >> ${SEPGSQL_STARTUP_LOG} 2>&1 < /dev/null - ret=$? - if [ $ret -eq 0 ]; then - echo_success - else - echo_failure - script_result=1 - fi - echo - rm -f "/var/run/${NAME}.pid" - rm -f "/var/lock/subsys/${NAME}.lock" -} - -do_status() { - /sbin/runuser sepgsql -- -c "${SEPGSQL_CTL} -D ${SEPGSQL_DATA} status" 2>/dev/null \ - | head -1 | sed "s/^sepg_ctl:/${NAME}:/g" - - if [ ${PIPESTATUS[0]} -ne 0 ]; then - script_result=3 - test -e "/var/run/${NAME}.pid" && script_result=1 - test -e "/var/lock/subsys/${NAME}.lock" && script_result=2 - fi -} - -do_condrestart() { - cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -- -c "${SEPGSQL_CTL} -D ${SEPGSQL_DATA} status" &>/dev/null && do_stop && do_start -} - -do_condstop() { - cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -- -c "${SEPGSQL_CTL} -D ${SEPGSQL_DATA} status" &>/dev/null && do_stop -} - -do_reload() { - echo -n $"Reloading ${NAME} service: " - cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -- -c "${SEPGSQL_CTL} -D ${SEPGSQL_DATA} reload" &>/dev/null < /dev/null - if [ $? -eq 0 ]; then - echo_success - else - echo_failure - script_result=1 - fi - echo -} - -do_initdb() { - echo -n $"Initializing database: " - - if [ -f "${SEPGSQL_DATA}/PG_VERSION" ]; then - echo_failure - echo - echo "HINT: Data directory is not empty" - script_result=1 - else - if [ ! -e "${SEPGSQL_DATA}" -a ! -h "${SEPGSQL_DATA}" ]; then - mkdir -p "${SEPGSQL_DATA}" || exit 1 - chown sepgsql:sepgsql "${SEPGSQL_DATA}" - chmod 600 "${SEPGSQL_DATA}" - fi - # cleanup SELinux labeling for "${SEPGSQL_DATA}" - test -x /sbin/restorecon && /sbin/restorecon -R "${SEPGSQL_DATA}" - # Initialize the database - /sbin/runuser -- sepgsql -c "${SEPGSQL_CTL} initdb -o '--enable-selinux --pgdata=${SEPGSQL_DATA} --auth=ident' -D ${SEPGSQL_DATA}" \ - >> "${SEPGSQL_STARTUP_LOG}" 2>&1 < /dev/null - if [ -f "${SEPGSQL_DATA}/PG_VERSION" ]; then - echo_success - else - echo_failure - script_result=1 - fi - echo - fi -} - -# see how we were called. -case "$1" in - start) - do_start - ;; - stop) - do_stop - ;; - status) - do_status - ;; - restart) - do_stop - do_start - ;; - condrestart) - do_condrestart - ;; - condstop) - do_condstop - ;; - reload|force-reload) - do_reload - ;; - initdb) - do_initdb - ;; - *) - echo $"Usage: $0 {start|stop|status|restart|condrestart|condstop|reload|force-reload|initdb}" - exit 1 - ;; -esac - -exit $script_result diff --git a/sepostgresql.logrotate b/sepostgresql.logrotate deleted file mode 100644 index 3ed7081..0000000 --- a/sepostgresql.logrotate +++ /dev/null @@ -1,11 +0,0 @@ -# logrotate configuration for SE-PostgreSQL - -/var/log/sepostgresql.log { - rotate 4 - compress - size 4M - notifempty - postrotate - /sbin/service sepostgresql restart >& /dev/null - endscript -} diff --git a/sepostgresql.spec b/sepostgresql.spec deleted file mode 100644 index dc6125e..0000000 --- a/sepostgresql.spec +++ /dev/null @@ -1,485 +0,0 @@ -# -# Security Enhanced PostgreSQL (SE-PostgreSQL) -# -# Copyright 2007 KaiGai Kohei -# ----------------------------------------------------- - -# SE-PostgreSQL status extension -%define selinux_policy_stores targeted mls - -%{!?ssl:%define ssl 1} - -Summary: Security Enhanced PostgreSQL -Name: sepostgresql -Version: 9.0.3 -Release: 20110416%{?dist} -License: PostgreSQL -Group: Applications/Databases -Url: http://code.google.com/p/sepgsql/ -Buildroot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) -Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.gz -Source1: sepostgresql.init -Source2: sepostgresql.8 -Source3: sepostgresql.logrotate -Patch0: sepostgresql-fedora-prefix.patch -Patch1: sepostgresql-9.0-fullset.patch -BuildRequires: perl glibc-devel bison flex readline-devel zlib-devel >= 1.0.4 -BuildRequires: checkpolicy libselinux-devel >= 2.0.96 audit-libs-devel -BuildRequires: selinux-policy >= 3.6.8 -%if %{ssl} -BuildRequires: openssl-devel -%endif -Requires(pre): shadow-utils -Requires(post): policycoreutils /sbin/chkconfig -Requires(preun): /sbin/chkconfig /sbin/service -Requires(postun): policycoreutils -Requires: policycoreutils >= 2.0.16 libselinux >= 2.0.96 -Requires: selinux-policy >= 3.6.8 -Requires: tzdata logrotate - -%description -Security Enhanced PostgreSQL is an extension of PostgreSQL -based on SELinux security policy, that applies fine grained -mandatory access control to many objects within the database, -and takes advantage of user authorization integrated within -the operating system. SE-PostgreSQL works as a userspace -reference monitor to check any SQL query. - -%prep -%setup -q -n postgresql-%{version} -%patch0 -p1 -%patch1 -p1 - -%build -CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS -CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS - -# build SE-PostgreSQL -%configure --disable-rpath \ - --enable-selinux \ -%if %{ssl} - --with-openssl \ -%endif - --enable-debug \ - --enable-cassert \ - --with-system-tzdata=/usr/share/zoneinfo - -# parallel build, if possible -rm -f src/Makefile.custom -make %{?_smp_mflags} - -%install -rm -rf %{buildroot} - -make DESTDIR=%{buildroot} install - -# avoid to conflict with native postgresql package -mv %{buildroot}%{_bindir} %{buildroot}%{_bindir}.orig -install -d %{buildroot}%{_bindir}/ -mv %{buildroot}%{_bindir}.orig/initdb %{buildroot}%{_bindir}/initdb.sepgsql -mv %{buildroot}%{_bindir}.orig/pg_ctl %{buildroot}%{_bindir}/sepg_ctl -mv %{buildroot}%{_bindir}.orig/postgres %{buildroot}%{_bindir}/sepostgres -mv %{buildroot}%{_bindir}.orig/pg_dump %{buildroot}%{_bindir}/sepg_dump -mv %{buildroot}%{_bindir}.orig/pg_dumpall %{buildroot}%{_bindir}/sepg_dumpall -mv %{buildroot}%{_bindir}.orig/pg_restore %{buildroot}%{_bindir}/sepg_restore - -mv %{buildroot}%{_libdir} %{buildroot}%{_libdir}.orig -install -d %{buildroot}%{_libdir}/sepgsql -mv %{buildroot}%{_libdir}.orig/sepgsql/dict_snowball.so \ - %{buildroot}%{_libdir}.orig/sepgsql/plpgsql.so \ - %{buildroot}%{_libdir}.orig/sepgsql/*_and_*.so \ - %{buildroot}%{_libdir}.orig/sepgsql/euc2004_sjis2004.so \ - %{buildroot}%{_libdir}.orig/sepgsql/libpqwalreceiver.so \ - %{buildroot}%{_libdir}/sepgsql - -# remove unnecessary files -rm -rf %{buildroot}%{_bindir}.orig -rm -rf %{buildroot}%{_libdir}.orig -rm -rf %{buildroot}%{_includedir} -rm -rf %{buildroot}%{_datadir}/doc -rm -rf %{buildroot}%{_datadir}/sepgsql/timezone -rm -rf %{buildroot}%{_mandir} - -# /var/lib/sepgsql -install -d -m 700 %{buildroot}%{_localstatedir}/lib/sepgsql -install -d -m 700 %{buildroot}%{_localstatedir}/lib/sepgsql/data -install -d -m 700 %{buildroot}%{_localstatedir}/lib/sepgsql/backups - -# /etc/rc.d/init.d/* -mkdir -p %{buildroot}%{_initrddir} -install -p -m 755 %{SOURCE1} %{buildroot}%{_initrddir}/sepostgresql - -# /usr/share/man/* -mkdir -p %{buildroot}%{_mandir}/man8 -install -p -m 644 %{SOURCE2} %{buildroot}%{_mandir}/man8 - -# /etc/logrotate.d/ -mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d -install -p -m 644 %{SOURCE3} %{buildroot}%{_sysconfdir}/logrotate.d/sepostgresql - -%clean -rm -rf %{buildroot} - -%pre -getent group sepgsql >/dev/null || groupadd -r sepgsql -getent passwd sepgsql >/dev/null || \ - useradd -r -g sepgsql -s /bin/bash -c "SE-PostgreSQL" sepgsql -exit 0 - -%post -/sbin/chkconfig --add %{name} -/sbin/ldconfig - -# Fix up non-standard file contexts -/sbin/fixfiles -R %{name} restore || : -/sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : - -%preun -if [ $1 -eq 0 ]; then # rpm -e case - /sbin/service %{name} condstop >/dev/null 2>&1 - /sbin/chkconfig --del %{name} -fi - -%postun -/sbin/ldconfig -if [ $1 -ge 1 ]; then # rpm -U case - /sbin/service %{name} condrestart >/dev/null 2>&1 || : -fi -if [ $1 -eq 0 ]; then # rpm -e case - /sbin/fixfiles -R %{name} restore || : - test -d %{_localstatedir}/lib/sepgsql && \ - /sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : -fi - -%files -%defattr(-,root,root,-) -%doc COPYRIGHT README -%{_initrddir}/sepostgresql -%{_sysconfdir}/logrotate.d/sepostgresql -%{_bindir}/initdb.sepgsql -%{_bindir}/sepg_ctl -%{_bindir}/sepostgres -%{_bindir}/sepg_dump -%{_bindir}/sepg_dumpall -%{_bindir}/sepg_restore -%{_libdir}/sepgsql/*.so -%{_mandir}/man8/sepostgresql.* -%dir %{_datadir}/sepgsql -%{_datadir}/sepgsql/postgres.bki -%{_datadir}/sepgsql/postgres.description -%{_datadir}/sepgsql/postgres.shdescription -%{_datadir}/sepgsql/system_views.sql -%{_datadir}/sepgsql/*.sample -%{_datadir}/sepgsql/snowball_create.sql -%{_datadir}/sepgsql/timezonesets/ -%{_datadir}/sepgsql/tsearch_data/ -%{_datadir}/sepgsql/conversion_create.sql -%{_datadir}/sepgsql/information_schema.sql -%{_datadir}/sepgsql/sql_features.txt -%attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql -%attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/data -%attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups - -%changelog -* Sat Jan 14 2012 Fedora Release Engineering - 9.0.3-20110416 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild - -* Fri Apr 15 2011 KaiGai Kohei - 9.0.3-20110415 -- upgrade base version to 9.0.3 -- initial labeling logic was revised to use selabel_lookup() - -* Wed Feb 09 2011 Fedora Release Engineering - 9.0.1-20101008 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild - -* Thu Oct 7 2010 KaiGai Kohei - 9.0.1-20101007 -- upgrade base version to 9.0.1 - -* Tue Oct 5 2010 KaiGai Kohei - 9.0.0-20101005 -- upgrade base version to 9.0.0 - -* Tue May 11 2010 KaiGai Kohei - 9.0.0-20100511 -- upgrade base version to 9.0beta1 - -* Sun Apr 4 2010 KaiGai Kohei - 9.0.0-20100404 -- upgrade base version 8.4.3->9.0alpha5 - -* 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 - -* Wed Dec 16 2009 KaiGai Kohei - 8.4.2-2487 -- upgrade base version 8.4.1->8.4.2 - -* Fri Dec 8 2009 KaiGai Kohei - 8.4.1-2464 -- rework: backport features from v8.5devel tree -- fixbug: selinux netlink receiver process didn't have correct ps display - -* Fri Sep 11 2009 KaiGai Kohei - 8.4.1-2305 -- Upgrade base SE-PostgreSQL v8.4.0->v8.4.1 -- rework: backport features from v8.5devel tree - -* Fri Aug 21 2009 Tomas Mraz - 8.4.0-2238 -- rebuilt with new openssl - -* Wed Aug 19 2009 KaiGai Kohei - 8.4.0-2237 -- Upgrade SE-PostgreSQL to 8.4.x series - -* Sun Jul 26 2009 Fedora Release Engineering - 8.3.7-1991 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild - -* Tue Jun 9 2009 KaiGai Kohei - 8.3.7-1990 -- backport features from v8.4devel, it also needs libselinux-2.0.80 - -* Fri Apr 17 2009 KaiGai Kohei - 8.3.7-1772 -- bugfix: /etc/init.d/sepostgresql initdb didn't work correctly - -* Fri Mar 27 2009 KaiGai Kohei - 8.3.7-1770 -- upgrade base PostgreSQL versin 8.3.6->8.3.7 -- backport features from v8.4devel - -* Thu Feb 26 2009 KaiGai Kohei - 8.3.6-2.1635 -- bugfix: possible information leak by the order of permission checks - in row level permission checks. - -* Wed Feb 25 2009 Fedora Release Engineering - 8.3.6-3.1518 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild - -* Fri Feb 6 2009 - 8.3.6-2.1523 -- upgrade base PostgreSQL version 8.3.5->8.3.6 -- backport features from 8.4devel tree -- security policy fix for Fedora 9 - -* Sat Jan 17 2009 Tomas Mraz - 8.3.5-2.1183 -- rebuild with new openssl - -* Wed Nov 5 2008 - 8.3.5-2.1182 -- upgrade base PostgreSQL version 8.3.4->8.3.5 -- backport cumulative bugfixes from 8.4devel series - -* Thu Oct 2 2008 - 8.3.4-2.1076 -- bugfix: "(null)" audit logs for non-cached decision making. -- A hook is added for "COPY TO/FROM " cases. - -* Sat Sep 27 2008 - 8.3.4-2.1066 -- update base version to 8.3.4 -- sepostgresql.pp was marked as obsolute - -* Tue Sep 23 2008 - 8.3.3-2.1043 -- bugfix: a case when INSERT a FK reference to invisible PK - -* Wed Aug 13 2008 - 8.3.3-2.964 -- bugfix: trusted procedure invokation - -* Fri Jul 11 2008 - 8.3.3-2.952 -- Security policy module updates - -* Fri Jul 11 2008 - 8.3.3-2.945 -- Add OpenSSL support -- backport 8.4devel fixes - -* Sun Jun 15 2008 - 8.3.3-2.889 -- backport 8.4devel features. - -* Fri Jun 13 2008 - 8.3.3-2.869 -- upgrade base PostgreSQL 8.3.1 -> 8.3.3 - -* Wed Apr 30 2008 - 8.3.1-2.197 -- Inconsistent version number format at Changelogs - -* Wed Apr 30 2008 - 8.3.1-2.196 -- BUGFIX: ROW-level control did not work correctly on TRUNCATE - -* Sun Mar 9 2008 - 8.3.0-2.129 -- BUGFIX: more conprehensive fixes in "SELECT COUNT(*) ..." - -* Sun Mar 2 2008 - 8.3.0-2.120 -- BUGFIX: CREATE TABLE statement with explicit labeled columns -- BUGFIX: SELECT count(*) does not filter unallowed tuples - -* Wed Feb 27 2008 - 8.3.0-2.117 -- ".beta" removed. - -* Wed Feb 27 2008 - 8.3.0-2.114 -- Security policy updates - -* Tue Feb 26 2008 - 8.3.0-2.113 -- BUGFIX: CREATE/ALTER TABLE with CONTEXT='...' did nothing. - -* Thu Feb 7 2008 - 8.3.0-2.108 -- add /etc/logrotate.d/sepostgresql - -* Thu Feb 7 2008 - 8.3.0-2.105 -- update base version to stable 8.3.0 -- add tzdata dependency -- allow db_database:{get_param set_param} for generic domain -- error message cleanups -- Improve large object hooks in PGACE framework -- BUGFIX: db_blob:{drop} was checked at loread() -- BUGFIX: incorrect permission in DELETE with RETURNING clause -- incorrect permission when we read and update security_context in same time. - -* Fri Jan 25 2008 - 8.3RC2-2.62 -- BUGFIX: add handling to invalid contexts already stored - -* Tue Jan 22 2008 - 8.3RC2-2.56 -- BUGFIX: lack of locks when refering buffer pages at update/delete hooks -- BUGFIX: explicit labeling using SELECT ... INTO statement. - -* Sun Jan 20 2008 - 8.3RC2-2.52 -- shares /usr/lib/pgsql/*.so libraries, with original postgresql. - -* Thu Jan 10 2008 - 8.3RC1-2.37 -- add sepg_dump/sepg_dumpall support for 8.3base package. - -* Mon Nov 26 2007 - 8.3beta3-2.0 -- Branch from 8.2.x tree - -* Wed Nov 21 2007 - 8.2.5-1.66 -- Add a policy module hotfix for labeled networking - -* Thu Nov 1 2007 - 8.2.5-1.51 -- Re-organize repository to prepare to branch 8.3.x based tree. - (no differences from 8.2.5-1.33) - -* Wed Oct 17 2007 - 8.2.5-1.33 -- Fix bug: security context was not canonicalized - when irregular context (but interpretable) was inputed. - -* Mon Oct 15 2007 - 8.2.5-1.31 -- Fix bug: type definitions of security_label_to_text() - and text_to_security_label() are mismatched. - -* Sat Sep 22 2007 - 8.2.5-1.23 -- update base PostgreSQL to 8.2.5 - -* Mon Sep 1 2007 - 8.2.4-1.0 -- mark as SE-PostgreSQL 8.2.4-1.0 - -* Thu Aug 28 2007 - 8.2.4-0.434.beta -- add Requires: postgresql-server, instead of Conflicts: tag - (Some sharable files are removed from sepostgresql package) - -* Fri Aug 24 2007 - 8.2.4-0.429.beta -- add policycoreutils to Requires(post/postun) -- upstreamed selinux-policy got SE-PostgreSQL related object classes definition. - -* Sat Aug 18 2007 - 8.2.4-0.427.beta -- sepg_dumpall uses /usr/bin/sepg_dump - -* Fri Aug 17 2007 - 8.2.4-0.423.beta -- fix policy not to execute sepgsql_user_proc_t from administrative domain - -* Fri Aug 10 2007 - 8.2.4-0.418.beta -- object classes are renamed with "db_" prefix -- /etc/init.d/sepostgresql script is improved. - -* Thu Aug 2 2007 - 8.2.4-0.409.beta -- specfile updated based on the following comments - https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=249522#c5 - -* Mon Jul 30 2007 - 8.2.4-0.407.beta -- fix spec file based on Fedora reviewing process -- add rawhide support - -* Mon Jul 23 2007 - 8.2.4-0.402.beta -- add manpage of sepostgresql -- fix specfile convention for Fedora suitable - -* Sun Jul 15 2007 - 8.2.4-0.398.beta -- SECCLASS_DATABASE is updated (fc7->62, fc6->61) - -* Sun Jul 1 2007 - 8.2.4-0.391.beta -- Mark as a beta version. - -* Sat Jun 30 2007 - 8.2.4-0.384.alpha -- add fallback context support with $SEPGSQL_FALLBACK_CONTEXT -- add sepgsql_enable_users_ddl boolean to restrict sepgsql_sysobj_t -- BUGFIX: incorrect inherited attribute expanding for RECORD type (attno=0) -- BUGFIX: trigger functions were not checked in COPY FROM statement - -* Tue Jun 26 2007 - 8.2.4-0.376.alpha -- add pgaceExecutorStart() to hook ExecutorStart() - -* Mon Jun 25 2007 - 8.2.4-0.372.alpha -- add table name prefix for column name on audit messages -- use security_label_raw_in as an alternative for security_label_in -- add hook for query execution path with SPI_ interface -- add trigger function suppoer -- BUGFIX: remove unnecessary checks for COPY TO/FROM on non-table relation -- BUGFIX: remove unnecessary checks for LOCK on non-table relation -- BUGFIX: incorrect object id for tuples within pg_security -- BUGFIX: CommandCounterIncrement() might be called during heap_create_with_catalog. -- BUGFIX: correct self-deadlock -- update security policy: sepgsql_sysobj_t, sepgsql_user_proc_t, sepgsql_ro_blob_t - -* Tue Jun 19 2007 - 8.2.4-0.351.alpha -- BUGFIX: sepgsql_compute_avc_datum() accessed userspace AVC without - holding any lock. -- improve build scripts. - -* Sat Jun 16 2007 - 8.2.4-0.320.alpha -- update: sepostgresql.pp security policy fot strict/mls suitable -- BUGFIX: column:drop evaluation for ALTER TABLE tbl DROP col; statement -- add --enable-security option for pg_dumpall command -- add {use} permission for table/column/tuple object classes - -* Tue May 29 2007 - 8.2.4-0.306.alpha -- BUGFIX: RangeTblEntry->requiredPerms are polluted. - -* Sun May 27 2007 - 8.2.4-0.304.alpha -- add support for dynamic object class/access vector mapping -- BUGFIX: Lack of implicit labeling on COPY FROM statement for system catalogs -- BUGFIX: Incorrect security context handling for inherited tables - -* Fri May 25 2007 - 8.2.4-0.292.alpha -- add pg_dump/pg_dumpall/pg_restore with --enable-security option -- add support on OUTER JOIN by rewriting query. -- add security_context support on COPY TO/FROM statement -- add unlabeled security context support (enable to obtain /selinux/initial_contexts/*) -- BUGFIX: lack of checks on JOIN ON condition -- BUGFIX: pseudo relation object (sequence, toast, ...) are not handled as database obj. -- BUGFIX: lack of tuple:insert checks at COPY FROM statement -- BUGFIX: server crash when CREATE TABLE command with newly defined CONTEXT = '...'. - -* Wed May 16 2007 - 8.2.4-0.266.alpha -- BUGFIX: incorrect security context of newly generated system object. -- BUGFIX: missing error text when audit log is disabled. -- BUGFIX: incorrect Oid of newly generated tuples within pg_security. -- BUGFIX: sepgsql_enable_audittuple is misconditioned. -- add checks for T_RowExpr/T_RowCompareExpr/T_BooleanTest - T_DistinctExpr/T_ConvertRowtypeExpr -- add support CONTEXT = 'xxx' for CREATE TABLE/FUNCTION/DATABASE statement - -* Sun Apr 30 2007 - 8.2.4-0.240.alpha -- update base version 8.2.3 -> 8.2.4 -- BUGFIX: unexpected expose in OUTER JOIN statement. - add rewrite OUTER JOIN into SUBQUERY to ensure filtering violated tuples. -- BUGFIX: strange operation in text_to_security_label() -- BUGFIX: infinite recursive call on security label -> oid mapping -- BUGFIX: sepgsql_avc_init() is called in policy state monitoring process - to avoid nonsense initialization of avc_shmem. - -* Fri Apr 27 2007 - 8.2.3-0.232.alpha -- object class numbers were redefined. (SECCLASS_DATABASE got into 61) -- is_selinux_enabled() was cached on the shared memory segment. -- BUGFIX: server went into infinit loop on foreign key constraint. - -* Mon Apr 16 2007 - 8.2.3-0.226.alpha -- BUGFIX: cases when several variables with same type in a single table - -* Sat Apr 07 2007 - 8.2.3-0.214.alpha -- add the first implementation of SE-PostgreSQL on PGACE framework - -* Wed Mar 21 2007 - 8.2.3-0.212.alpha -- BUGFIX: SetOperation didn't handle its subquery correctly. - So, it caused server crash. - -* Wed Mar 07 2007 - 8.2.3-0.209.alpha -- BUGFIX: var->varlevelsup was ignored, so outer references - from subqueries cause a fault. - -* Tue Feb 27 2007 - 8.2.3-0.178.alpha -- Initial RPM build diff --git a/sources b/sources deleted file mode 100644 index 8ba5714..0000000 --- a/sources +++ /dev/null @@ -1 +0,0 @@ -56386ded2d5dcd8a4ceef0da81c3d22c postgresql-9.0.3.tar.gz