From f4c4335a4aaf5f2ee6e741cdf4f5c8e338d86a2f Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Tue, 23 Oct 2012 18:07:26 -0300 Subject: [PATCH] Add context info to OAT_POST_CREATE security hook ... and have sepgsql use it to determine whether to check permissions during certain operations. Indexes that are being created as a result of REINDEX, for instance, do not need to have their permissions checked; they were already checked when the index was created. Author: KaiGai Kohei, slightly revised by me --- contrib/sepgsql/expected/ddl.out | 53 ++++++++ contrib/sepgsql/hooks.c | 125 ++++++------------- contrib/sepgsql/relation.c | 194 +++++++++++++++++++++++++---- contrib/sepgsql/sepgsql.h | 2 +- contrib/sepgsql/sql/ddl.sql | 12 ++ doc/src/sgml/sepgsql.sgml | 6 + src/backend/bootstrap/bootparse.y | 3 +- src/backend/catalog/heap.c | 14 ++- src/backend/catalog/index.c | 15 ++- src/backend/catalog/toasting.c | 3 +- src/backend/commands/cluster.c | 1 + src/backend/commands/indexcmds.c | 2 +- src/backend/commands/tablecmds.c | 3 +- src/include/catalog/heap.h | 3 +- src/include/catalog/index.h | 3 +- src/include/catalog/objectaccess.h | 13 ++ 16 files changed, 336 insertions(+), 116 deletions(-) diff --git a/contrib/sepgsql/expected/ddl.out b/contrib/sepgsql/expected/ddl.out index e7a8d9c301..1f7ea886b0 100644 --- a/contrib/sepgsql/expected/ddl.out +++ b/contrib/sepgsql/expected/ddl.out @@ -34,6 +34,8 @@ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column ctid" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column x" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column y" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table" ALTER TABLE regtest_table ADD COLUMN z int; LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column z" CREATE TABLE regtest_table_2 (a int) WITH OIDS; @@ -93,6 +95,55 @@ LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfine LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_2(integer)" RESET SESSION AUTHORIZATION; -- +-- ALTER and CREATE/DROP extra attribute permissions +-- +CREATE TABLE regtest_table_4 (x int primary key, y int, z int); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column tableoid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column cmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column xmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column cmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column xmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column ctid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column x" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column y" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column z" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" +CREATE INDEX regtest_index_tbl4_y ON regtest_table_4(y); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" +CREATE INDEX regtest_index_tbl4_z ON regtest_table_4(z); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" +ALTER TABLE regtest_table_4 ALTER COLUMN y TYPE float; +DROP INDEX regtest_index_tbl4_y; +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" +ALTER TABLE regtest_table_4 + ADD CONSTRAINT regtest_tbl4_con EXCLUDE USING btree (z WITH =); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" +DROP TABLE regtest_table_4 CASCADE; +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column tableoid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column cmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column xmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column cmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column xmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column ctid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column x" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column y" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column z" +-- -- DROP Permission checks (with clean-up) -- DROP FUNCTION regtest_func(text,int[]); @@ -115,6 +166,8 @@ DROP TABLE regtest_table; LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq" LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column tableoid" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column cmax" diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index f3cf1c5f88..ab55d6ea4b 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -38,7 +38,6 @@ void _PG_init(void); static object_access_hook_type next_object_access_hook = NULL; static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; -static ExecutorStart_hook_type next_ExecutorStart_hook = NULL; /* * Contextual information on DDL commands @@ -97,53 +96,55 @@ sepgsql_object_access(ObjectAccessType access, switch (access) { case OAT_POST_CREATE: - switch (classId) { - case DatabaseRelationId: - sepgsql_database_post_create(objectId, - sepgsql_context_info.createdb_dtemplate); - break; + ObjectAccessPostCreate *pc_arg = arg; + bool is_internal; - case NamespaceRelationId: - sepgsql_schema_post_create(objectId); - break; + is_internal = pc_arg ? pc_arg->is_internal : false; - case RelationRelationId: - if (subId == 0) - { - /* - * All cases we want to apply permission checks on - * creation of a new relation are invocation of the - * heap_create_with_catalog via DefineRelation or - * OpenIntoRel. Elsewhere, we need neither assignment - * of security label nor permission checks. - */ - switch (sepgsql_context_info.cmdtype) + switch (classId) + { + case DatabaseRelationId: + Assert(!is_internal); + sepgsql_database_post_create(objectId, + sepgsql_context_info.createdb_dtemplate); + break; + + case NamespaceRelationId: + Assert(!is_internal); + sepgsql_schema_post_create(objectId); + break; + + case RelationRelationId: + if (subId == 0) { - case T_CreateStmt: - case T_ViewStmt: - case T_CreateSeqStmt: - case T_CompositeTypeStmt: - case T_CreateForeignTableStmt: - case T_SelectStmt: - sepgsql_relation_post_create(objectId); - break; - default: - /* via make_new_heap() */ + /* + * The cases in which we want to apply permission + * checks on creation of a new relation correspond + * to direct user invocation. For internal uses, + * that is creation of toast tables, index rebuild + * or ALTER TABLE commands, we need neither + * assignment of security labels nor permission + * checks. + */ + if (is_internal) break; + + sepgsql_relation_post_create(objectId); } - } - else - sepgsql_attribute_post_create(objectId, subId); - break; + else + sepgsql_attribute_post_create(objectId, subId); + break; - case ProcedureRelationId: - sepgsql_proc_post_create(objectId); - break; + case ProcedureRelationId: + Assert(!is_internal); + sepgsql_proc_post_create(objectId); + break; - default: - /* Ignore unsupported object classes */ - break; + default: + /* Ignore unsupported object classes */ + break; + } } break; @@ -215,46 +216,6 @@ sepgsql_exec_check_perms(List *rangeTabls, bool abort) return true; } -/* - * sepgsql_executor_start - * - * It saves contextual information during ExecutorStart to distinguish - * a case with/without permission checks later. - */ -static void -sepgsql_executor_start(QueryDesc *queryDesc, int eflags) -{ - sepgsql_context_info_t saved_context_info = sepgsql_context_info; - - PG_TRY(); - { - if (queryDesc->operation == CMD_SELECT) - sepgsql_context_info.cmdtype = T_SelectStmt; - else if (queryDesc->operation == CMD_INSERT) - sepgsql_context_info.cmdtype = T_InsertStmt; - else if (queryDesc->operation == CMD_DELETE) - sepgsql_context_info.cmdtype = T_DeleteStmt; - else if (queryDesc->operation == CMD_UPDATE) - sepgsql_context_info.cmdtype = T_UpdateStmt; - - /* - * XXX - If queryDesc->operation is not above four cases, an error - * shall be raised on the following executor stage soon. - */ - if (next_ExecutorStart_hook) - (*next_ExecutorStart_hook) (queryDesc, eflags); - else - standard_ExecutorStart(queryDesc, eflags); - } - PG_CATCH(); - { - sepgsql_context_info = saved_context_info; - PG_RE_THROW(); - } - PG_END_TRY(); - sepgsql_context_info = saved_context_info; -} - /* * sepgsql_utility_command * @@ -425,10 +386,6 @@ _PG_init(void) next_ProcessUtility_hook = ProcessUtility_hook; ProcessUtility_hook = sepgsql_utility_command; - /* ExecutorStart hook */ - next_ExecutorStart_hook = ExecutorStart_hook; - ExecutorStart_hook = sepgsql_executor_start; - /* init contextual info */ memset(&sepgsql_context_info, 0, sizeof(sepgsql_context_info)); } diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c index 4ab7fc8be9..783f330d1c 100644 --- a/contrib/sepgsql/relation.c +++ b/contrib/sepgsql/relation.c @@ -23,11 +23,14 @@ #include "utils/fmgroids.h" #include "utils/catcache.h" #include "utils/lsyscache.h" +#include "utils/rel.h" #include "utils/syscache.h" #include "utils/tqual.h" #include "sepgsql.h" +static void sepgsql_index_modify(Oid indexOid); + /* * sepgsql_attribute_post_create * @@ -229,6 +232,23 @@ sepgsql_relation_post_create(Oid relOid) classForm = (Form_pg_class) GETSTRUCT(tuple); + /* ignore indexes on toast tables */ + if (classForm->relkind == RELKIND_INDEX && + classForm->relnamespace == PG_TOAST_NAMESPACE) + goto out; + + /* + * check db_schema:{add_name} permission of the namespace + */ + object.classId = NamespaceRelationId; + object.objectId = classForm->relnamespace; + object.objectSubId = 0; + sepgsql_avc_check_perms(&object, + SEPG_CLASS_DB_SCHEMA, + SEPG_DB_SCHEMA__ADD_NAME, + getObjectDescription(&object), + true); + switch (classForm->relkind) { case RELKIND_RELATION: @@ -243,22 +263,15 @@ sepgsql_relation_post_create(Oid relOid) tclass = SEPG_CLASS_DB_VIEW; tclass_text = "view"; break; + case RELKIND_INDEX: + /* deal with indexes specially; no need for tclass */ + sepgsql_index_modify(relOid); + goto out; default: + /* ignore other relkinds */ goto out; } - /* - * check db_schema:{add_name} permission of the namespace - */ - object.classId = NamespaceRelationId; - object.objectId = classForm->relnamespace; - object.objectSubId = 0; - sepgsql_avc_check_perms(&object, - SEPG_CLASS_DB_SCHEMA, - SEPG_DB_SCHEMA__ADD_NAME, - getObjectDescription(&object), - true); - /* * Compute a default security label when we create a new relation object * under the specified namespace. @@ -342,6 +355,7 @@ sepgsql_relation_post_create(Oid relOid) heap_close(arel, AccessShareLock); } pfree(rcontext); + out: systable_endscan(sscan); heap_close(rel, AccessShareLock); @@ -357,18 +371,31 @@ sepgsql_relation_drop(Oid relOid) { ObjectAddress object; char *audit_name; - uint16_t tclass = 0; + uint16_t tclass; char relkind; relkind = get_rel_relkind(relOid); - if (relkind == RELKIND_RELATION) - tclass = SEPG_CLASS_DB_TABLE; - else if (relkind == RELKIND_SEQUENCE) - tclass = SEPG_CLASS_DB_SEQUENCE; - else if (relkind == RELKIND_VIEW) - tclass = SEPG_CLASS_DB_VIEW; - else - return; + 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_INDEX: + /* ignore indexes on toast tables */ + if (get_rel_namespace(relOid) == PG_TOAST_NAMESPACE) + return; + /* other indexes are handled specially below; no need for tclass */ + break; + default: + /* ignore other relkinds */ + return; + } /* * check db_schema:{remove_name} permission @@ -385,6 +412,13 @@ sepgsql_relation_drop(Oid relOid) true); pfree(audit_name); + /* deal with indexes specially */ + if (relkind == RELKIND_INDEX) + { + sepgsql_index_modify(relOid); + return; + } + /* * check db_table/sequence/view:{drop} permission */ @@ -486,3 +520,121 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel) true); pfree(audit_name); } + +/* + * sepgsql_relation_setattr + * + * It checks privileges to set attribute of the supplied relation + */ +void +sepgsql_relation_setattr(Oid relOid) +{ + ObjectAddress object; + char *audit_name; + uint16_t tclass; + + switch (get_rel_relkind(relOid)) + { + 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_INDEX: + /* deal with indexes specially */ + sepgsql_index_modify(relOid); + return; + default: + /* other relkinds don't need additional work */ + return; + } + + object.classId = RelationRelationId; + object.objectId = relOid; + object.objectSubId = 0; + audit_name = getObjectDescription(&object); + + /* + * XXX - we should add checks related to namespace stuff, when + * object_access_hook get support for ALTER statement. Right now, there is + * no invocation path on ALTER ... RENAME TO / SET SCHEMA. + */ + + /* + * check db_xxx:{setattr} permission + */ + sepgsql_avc_check_perms(&object, + tclass, + SEPG_DB_TABLE__SETATTR, + audit_name, + true); + pfree(audit_name); +} + +/* + * sepgsql_relation_setattr_extra + * + * It checks permission of the relation being referenced by extra attributes, + * such as pg_index entries. Like core PostgreSQL, sepgsql also does not deal + * with such entries as individual "objects", thus, modification of these + * entries shall be considered as setting an attribute of the underlying + * relation. + */ +static void +sepgsql_relation_setattr_extra(Relation catalog, + Oid catindex_id, + Oid extra_oid, + AttrNumber anum_relation_id, + AttrNumber anum_extra_id) +{ + ScanKeyData skey; + SysScanDesc sscan; + HeapTuple tuple; + Datum datum; + bool isnull; + + ScanKeyInit(&skey, anum_extra_id, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(extra_oid)); + + sscan = systable_beginscan(catalog, catindex_id, true, + SnapshotSelf, 1, &skey); + tuple = systable_getnext(sscan); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "catalog lookup failed for object %u in catalog \"%s\"", + extra_oid, RelationGetRelationName(catalog)); + + datum = heap_getattr(tuple, anum_relation_id, + RelationGetDescr(catalog), &isnull); + Assert(!isnull); + + sepgsql_relation_setattr(DatumGetObjectId(datum)); + + systable_endscan(sscan); +} + +/* + * sepgsql_index_modify + * Handle index create, update, drop + * + * Unlike other relation kinds, indexes do not have their own security labels, + * so instead of doing checks directly, treat them as extra attributes of their + * owning tables; so check 'setattr' permissions on the table. + */ +static void +sepgsql_index_modify(Oid indexOid) +{ + Relation catalog = heap_open(IndexRelationId, AccessShareLock); + + /* check db_table:{setattr} permission of the table being indexed */ + sepgsql_relation_setattr_extra(catalog, + IndexRelidIndexId, + indexOid, + Anum_pg_index_indrelid, + Anum_pg_index_indexrelid); + heap_close(catalog, AccessShareLock); +} diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index 9c89eaa893..b6dcb86e55 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -145,7 +145,6 @@ #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) @@ -312,6 +311,7 @@ extern void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, extern void sepgsql_relation_post_create(Oid relOid); extern void sepgsql_relation_drop(Oid relOid); extern void sepgsql_relation_relabel(Oid relOid, const char *seclabel); +extern void sepgsql_relation_setattr(Oid relOid); /* * proc.c diff --git a/contrib/sepgsql/sql/ddl.sql b/contrib/sepgsql/sql/ddl.sql index 8dd57e0eaf..5afe1ba193 100644 --- a/contrib/sepgsql/sql/ddl.sql +++ b/contrib/sepgsql/sql/ddl.sql @@ -59,6 +59,18 @@ CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql RESET SESSION AUTHORIZATION; +-- +-- ALTER and CREATE/DROP extra attribute permissions +-- +CREATE TABLE regtest_table_4 (x int primary key, y int, z int); +CREATE INDEX regtest_index_tbl4_y ON regtest_table_4(y); +CREATE INDEX regtest_index_tbl4_z ON regtest_table_4(z); +ALTER TABLE regtest_table_4 ALTER COLUMN y TYPE float; +DROP INDEX regtest_index_tbl4_y; +ALTER TABLE regtest_table_4 + ADD CONSTRAINT regtest_tbl4_con EXCLUDE USING btree (z WITH =); +DROP TABLE regtest_table_4 CASCADE; + -- -- DROP Permission checks (with clean-up) -- diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml index ff083a0659..522aa8b990 100644 --- a/doc/src/sgml/sepgsql.sgml +++ b/doc/src/sgml/sepgsql.sgml @@ -449,6 +449,12 @@ UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100; remove_name on the schema. + + When objects that are subsidiary of other objects (such as a table's indexes + or triggers) are created or dropped, setattr permission will be + checked on the main object, instead of the subsidiary object itself. + + When is executed, setattr and relabelfrom will be checked on the object being relabeled diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index ec634f1660..ec7786a2ae 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -247,7 +247,8 @@ Boot_CreateStmt: ONCOMMIT_NOOP, (Datum) 0, false, - true); + true, + false); elog(DEBUG4, "relation created with OID %u", id); } do_end(); diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index c80df418fa..6edd11fb98 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -985,7 +985,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, + bool is_internal) { Relation pg_class_desc; Relation new_rel_desc; @@ -1275,8 +1276,15 @@ heap_create_with_catalog(const char *relname, } /* Post creation hook for new relation */ - InvokeObjectAccessHook(OAT_POST_CREATE, - RelationRelationId, relid, 0, NULL); + if (object_access_hook) + { + ObjectAccessPostCreate post_create_args; + + memset(&post_create_args, 0, sizeof(ObjectAccessPostCreate)); + post_create_args.is_internal = is_internal; + (*object_access_hook)(OAT_POST_CREATE, RelationRelationId, + relid, 0, &post_create_args); + } /* * Store any supplied constraints and defaults. diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 972a528aec..756f6d918b 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -33,6 +33,7 @@ #include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/index.h" +#include "catalog/objectaccess.h" #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_operator.h" @@ -686,7 +687,8 @@ index_create(Relation heapRelation, bool initdeferred, bool allow_system_table_mods, bool skip_build, - bool concurrent) + bool concurrent, + bool is_internal) { Oid heapRelationId = RelationGetRelid(heapRelation); Relation pg_class; @@ -1018,6 +1020,17 @@ index_create(Relation heapRelation, Assert(!initdeferred); } + /* Post creation hook for new index */ + if (object_access_hook) + { + ObjectAccessPostCreate post_create_args; + + memset(&post_create_args, 0, sizeof(ObjectAccessPostCreate)); + post_create_args.is_internal = is_internal; + (*object_access_hook)(OAT_POST_CREATE, RelationRelationId, + indexRelationId, 0, &post_create_args); + } + /* * Advance the command counter so that we can see the newly-entered * catalog tuples for the index. diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 1feffd25ef..2979819e96 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -226,6 +226,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio ONCOMMIT_NOOP, reloptions, false, + true, true); Assert(toast_relid != InvalidOid); @@ -279,7 +280,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio rel->rd_rel->reltablespace, collationObjectId, classObjectId, coloptions, (Datum) 0, true, false, false, false, - true, false, false); + true, false, false, true); heap_close(toast_rel, NoLock); diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index cfec413d54..de71a3594d 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -643,6 +643,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) ONCOMMIT_NOOP, reloptions, false, + true, true); Assert(OIDNewHeap != InvalidOid); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index a58101ec6e..dd46cf93da 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -596,7 +596,7 @@ DefineIndex(IndexStmt *stmt, stmt->isconstraint, stmt->deferrable, stmt->initdeferred, allowSystemTableMods, skip_build || stmt->concurrent, - stmt->concurrent); + stmt->concurrent, !check_rights); /* Add any requested comment */ if (stmt->idxcomment != NULL) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 359d478592..378b29d525 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -630,7 +630,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) stmt->oncommit, reloptions, true, - allowSystemTableMods); + allowSystemTableMods, + false); /* Store inheritance information for new rel. */ StoreCatalogInheritance(relationId, inheritOids); diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index bc8c63a15e..1465456cc7 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -66,7 +66,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, + bool is_internal); extern void heap_create_init_fork(Relation rel); diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index eb417cecb7..298641baf7 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -50,7 +50,8 @@ extern Oid index_create(Relation heapRelation, bool initdeferred, bool allow_system_table_mods, bool skip_build, - bool concurrent); + bool concurrent, + bool is_internal); extern void index_constraint_create(Relation heapRelation, Oid indexRelationId, diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h index 3b40dbc492..b4b84a64d0 100644 --- a/src/include/catalog/objectaccess.h +++ b/src/include/catalog/objectaccess.h @@ -30,6 +30,19 @@ typedef enum ObjectAccessType OAT_DROP, } ObjectAccessType; +/* + * Arguments of OAT_POST_CREATE event + */ +typedef struct +{ + /* + * This flag informs extensions whether the context of this creation + * is invoked by user's operations, or not. E.g, it shall be dealt + * as internal stuff on toast tables or indexes due to type changes. + */ + bool is_internal; +} ObjectAccessPostCreate; + /* * Arguments of OAT_DROP event */ -- 2.40.0