[v9.2] sepgsql's DROP Permission checks
The attached patch adds OAT_DROP object-access-hook around permission
checks of object deletion.
Due to the previous drop statement reworks, the number of places to
put this hook is limited to these six points: RemoveObjects,
RemoveRelations, ATExecDropColumn, dropdb, DropTableSpace and
shdepDropOwned().
In sepgsql side, it checks {drop} permission for each object types,
and {remove_name} permission to the schema that owning the object
being removed. I'm still considering whether the drop permission
should be applied on objects being removed in cascade mode.
It is not difficult to implement. We can determine the bahavior on
object deletion with same manner of creation; that saves contextual
information using ProcessUtility hook.
At this moment, the current proposed patch does not apply checks on
cascaded deletion, according to SQL behavior. However, my concern is
that user can automatically have right to remove all the objects
underlying a partidular schema being removable, even if individual
tables or functions are not able to be removed.
So, my preference is sepgsql references dependency tables to check
{drop} permissions of involved objects, not only the target object.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
Attachments:
pgsql-v9.2-drop-permissions.v1.patchapplication/octet-stream; name=pgsql-v9.2-drop-permissions.v1.patchDownload
contrib/sepgsql/database.c | 27 ++++++
contrib/sepgsql/expected/create.out | 80 ------------------
contrib/sepgsql/expected/ddl.out | 152 +++++++++++++++++++++++++++++++++++
contrib/sepgsql/hooks.c | 24 ++++++
contrib/sepgsql/proc.c | 42 ++++++++++
contrib/sepgsql/relation.c | 119 +++++++++++++++++++++++++++
contrib/sepgsql/schema.c | 27 ++++++
contrib/sepgsql/sepgsql.h | 5 +
contrib/sepgsql/sql/create.sql | 46 -----------
contrib/sepgsql/sql/ddl.sql | 81 +++++++++++++++++++
contrib/sepgsql/test_sepgsql | 2 +-
doc/src/sgml/sepgsql.sgml | 14 +++
src/backend/catalog/pg_shdepend.c | 5 +
src/backend/commands/dbcommands.c | 3 +
src/backend/commands/dropcmds.c | 5 +
src/backend/commands/tablecmds.c | 7 ++
src/backend/commands/tablespace.c | 3 +
src/include/catalog/objectaccess.h | 4 +
18 files changed, 519 insertions(+), 127 deletions(-)
diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c
index be3a7be..0c395c4 100644
--- a/contrib/sepgsql/database.c
+++ b/contrib/sepgsql/database.c
@@ -119,6 +119,33 @@ sepgsql_database_post_create(Oid databaseId, const char *dtemplate)
}
/*
+ * sepgsql_database_drop
+ *
+ * It checks privileges to drop the supplied database
+ */
+void
+sepgsql_database_drop(Oid databaseId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_database:{drop} permission
+ */
+ object.classId = DatabaseRelationId;
+ object.objectId = databaseId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_DATABASE,
+ SEPG_DB_DATABASE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_database_relabel
*
* It checks privileges to relabel the supplied database with the `seclabel'
diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out
deleted file mode 100644
index 0f04a3e..0000000
--- a/contrib/sepgsql/expected/create.out
+++ /dev/null
@@ -1,80 +0,0 @@
---
--- Regression Test for Creation of Object Permission Checks
---
--- confirm required permissions using audit messages
-SELECT sepgsql_getcon(); -- confirm client privilege
- sepgsql_getcon
--------------------------------------------
- unconfined_u:unconfined_r:unconfined_t:s0
-(1 row)
-
-SET sepgsql.debug_audit = true;
-SET client_min_messages = LOG;
-CREATE DATABASE regtest_sepgsql_test_database;
-LOG: SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
-LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
-CREATE SCHEMA regtest_schema;
-LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
-SET search_path = regtest_schema, public;
-CREATE TABLE regtest_table (x serial primary key, y text);
-NOTICE: CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x"
-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_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq"
-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"
-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 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 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 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 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 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 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"
-NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for 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;
-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_2"
-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_2 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_2 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_2 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_2 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_2 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_2 column oid"
-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_2 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_2 column a"
--- corresponding toast table should not have label and permission checks
-ALTER TABLE regtest_table_2 ADD COLUMN b text;
-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_2 column b"
--- VACUUM FULL internally create a new table and swap them later.
-VACUUM FULL regtest_table;
-CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
-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_view_t:s0 tclass=db_view name="view regtest_view"
-CREATE SEQUENCE regtest_seq;
-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_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
-CREATE TYPE regtest_comptype AS (a int, b text);
-CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
- AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
-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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
-CREATE AGGREGATE regtest_agg (
- sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
-);
-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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
---
--- clean-up
---
-DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
-DROP SCHEMA IF EXISTS regtest_schema CASCADE;
-NOTICE: drop cascades to 7 other objects
-DETAIL: drop cascades to table regtest_table
-drop cascades to table regtest_table_2
-drop cascades to view regtest_view
-drop cascades to sequence regtest_seq
-drop cascades to type regtest_comptype
-drop cascades to function regtest_func(text,integer[])
-drop cascades to function regtest_agg(integer)
diff --git a/contrib/sepgsql/expected/ddl.out b/contrib/sepgsql/expected/ddl.out
new file mode 100644
index 0000000..8b42255
--- /dev/null
+++ b/contrib/sepgsql/expected/ddl.out
@@ -0,0 +1,152 @@
+--
+-- Regression Test for DDL of Object Permission Checks
+--
+-- confirm required permissions using audit messages
+SELECT sepgsql_getcon(); -- confirm client privilege
+ sepgsql_getcon
+-------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0
+(1 row)
+
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+--
+-- CREATE Permission checks
+--
+CREATE DATABASE regtest_sepgsql_test_database;
+LOG: SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
+LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
+CREATE USER regtest_sepgsql_test_user;
+CREATE SCHEMA regtest_schema;
+LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+GRANT ALL ON SCHEMA regtest_schema TO regtest_sepgsql_test_user;
+SET search_path = regtest_schema, public;
+CREATE TABLE regtest_table (x serial primary key, y text);
+NOTICE: CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x"
+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_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq"
+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"
+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 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 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 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 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 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 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"
+NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for 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;
+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_2"
+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_2 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_2 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_2 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_2 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_2 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_2 column oid"
+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_2 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_2 column a"
+-- corresponding toast table should not have label and permission checks
+ALTER TABLE regtest_table_2 ADD COLUMN b text;
+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_2 column b"
+-- VACUUM FULL internally create a new table and swap them later.
+VACUUM FULL regtest_table;
+CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
+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_view_t:s0 tclass=db_view name="view regtest_view"
+CREATE SEQUENCE regtest_seq;
+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_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
+CREATE TYPE regtest_comptype AS (a int, b text);
+CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
+CREATE AGGREGATE regtest_agg (
+ sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
+);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
+-- CREATE objects owned by others
+SET SESSION AUTHORIZATION regtest_sepgsql_test_user;
+SET search_path = regtest_schema, public;
+CREATE TABLE regtest_table_3 (x int, y serial);
+NOTICE: CREATE TABLE will create implicit sequence "regtest_table_3_y_seq" for serial column "regtest_table_3.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 { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_3_y_seq"
+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_3"
+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_3 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_3 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_3 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_3 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_3 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_3 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_3 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_3 column y"
+CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < 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 { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view_2"
+CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RETURN $1 * $1 < 100; END';
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_2(integer)"
+RESET SESSION AUTHORIZATION;
+--
+-- DROP Permission checks (with clean-up)
+--
+DROP FUNCTION regtest_func(text,int[]);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
+DROP AGGREGATE regtest_agg(int);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
+DROP SEQUENCE regtest_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 { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
+DROP VIEW regtest_view;
+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_view_t:s0 tclass=db_view name="view regtest_view"
+ALTER TABLE regtest_table DROP 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 column y"
+ALTER TABLE regtest_table_2 SET WITHOUT OIDS;
+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_2 column oid"
+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_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"
+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 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 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 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 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 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 column z"
+DROP OWNED BY regtest_sepgsql_test_user;
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_2(integer)"
+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_view_t:s0 tclass=db_view name="view regtest_view_2"
+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_3"
+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_3 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_3 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_3 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_3 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_3 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_3 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_3 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_3 column 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 { 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_3_y_seq"
+DROP DATABASE regtest_sepgsql_test_database;
+LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
+DROP USER regtest_sepgsql_test_user;
+DROP SCHEMA IF EXISTS regtest_schema CASCADE;
+LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table regtest_table_2
+drop cascades to type regtest_comptype
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 47437ba..5ba78c4 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -192,6 +192,30 @@ sepgsql_object_access(ObjectAccessType access,
}
break;
+ case OAT_DROP:
+ switch (classId)
+ {
+ case DatabaseRelationId:
+ sepgsql_database_drop(objectId);
+ break;
+ case NamespaceRelationId:
+ sepgsql_schema_drop(objectId);
+ break;
+ case RelationRelationId:
+ if (subId == 0)
+ sepgsql_relation_drop(objectId);
+ else
+ sepgsql_attribute_drop(objectId, subId);
+ break;
+ case ProcedureRelationId:
+ sepgsql_proc_drop(objectId);
+ break;
+ default:
+ /* Ignore unsupported object classes */
+ break;
+ }
+ break;
+
default:
elog(ERROR, "unexpected object access type: %d", (int) access);
break;
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
index b902797..1efbc90 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -131,6 +131,48 @@ sepgsql_proc_post_create(Oid functionId)
}
/*
+ * sepgsql_proc_drop
+ *
+ * It checks privileges to drop the supplied function.
+ */
+void
+sepgsql_proc_drop(Oid functionId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_schema:{remove_name} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = get_func_namespace(functionId);
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__REMOVE_NAME,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_procedure:{drop} permission
+ */
+ object.classId = ProcedureRelationId;
+ object.objectId = functionId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_DB_PROCEDURE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_proc_relabel
*
* It checks privileges to relabel the supplied function
diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c
index efce914..259be49 100644
--- a/contrib/sepgsql/relation.c
+++ b/contrib/sepgsql/relation.c
@@ -21,6 +21,7 @@
#include "commands/seclabel.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/tqual.h"
#include "sepgsql.h"
@@ -110,6 +111,36 @@ sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
}
/*
+ * sepgsql_attribute_drop
+ *
+ * It checks privileges to drop the supplied column.
+ */
+void
+sepgsql_attribute_drop(Oid relOid, AttrNumber attnum)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ if (get_rel_relkind(relOid) != RELKIND_RELATION)
+ return;
+
+ /*
+ * check db_column:{drop} permission
+ */
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attnum;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_DB_COLUMN__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_attribute_relabel
*
* It checks privileges to relabel the supplied column
@@ -310,6 +341,94 @@ out:
}
/*
+ * sepgsql_relation_drop
+ *
+ * It checks privileges to drop the supplied relation.
+ */
+void
+sepgsql_relation_drop(Oid relOid)
+{
+ ObjectAddress object;
+ char *audit_name;
+ uint16_t tclass = 0;
+ 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;
+
+ /*
+ * check db_schema:{remove_name} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = get_rel_namespace(relOid);
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__REMOVE_NAME,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_table/sequence/view:{drop} permission
+ */
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ tclass,
+ SEPG_DB_TABLE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_column:{drop} permission
+ */
+ if (relkind == RELKIND_RELATION)
+ {
+ Form_pg_attribute attForm;
+ CatCList *attrList;
+ HeapTuple atttup;
+ int i;
+
+ attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid));
+ for (i=0; i < attrList->n_members; i++)
+ {
+ atttup = &attrList->members[i]->tuple;
+ attForm = (Form_pg_attribute) GETSTRUCT(atttup);
+
+ if (attForm->attisdropped)
+ continue;
+
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attForm->attnum;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_DB_COLUMN__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+ }
+ ReleaseCatCacheList(attrList);
+ }
+}
+
+/*
* sepgsql_relation_relabel
*
* It checks privileges to relabel the supplied relation by the `seclabel'.
diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c
index 90dca1d..31d60ef 100644
--- a/contrib/sepgsql/schema.c
+++ b/contrib/sepgsql/schema.c
@@ -97,6 +97,33 @@ sepgsql_schema_post_create(Oid namespaceId)
}
/*
+ * sepgsql_schema_drop
+ *
+ * It checks privileges to drop the supplied schema object.
+ */
+void
+sepgsql_schema_drop(Oid namespaceId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_schema:{drop} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = namespaceId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_schema_relabel
*
* It checks privileges to relabel the supplied schema
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index c93da7a..a7a4532 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -288,27 +288,32 @@ extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort);
*/
extern void sepgsql_database_post_create(Oid databaseId,
const char *dtemplate);
+extern void sepgsql_database_drop(Oid databaseId);
extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel);
/*
* schema.c
*/
extern void sepgsql_schema_post_create(Oid namespaceId);
+extern void sepgsql_schema_drop(Oid namespaceId);
extern void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel);
/*
* relation.c
*/
extern void sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum);
+extern void sepgsql_attribute_drop(Oid relOid, AttrNumber attnum);
extern void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
const char *seclabel);
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);
/*
* proc.c
*/
extern void sepgsql_proc_post_create(Oid functionId);
+extern void sepgsql_proc_drop(Oid functionId);
extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel);
#endif /* SEPGSQL_H */
diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql
deleted file mode 100644
index b0695b4..0000000
--- a/contrib/sepgsql/sql/create.sql
+++ /dev/null
@@ -1,46 +0,0 @@
---
--- Regression Test for Creation of Object Permission Checks
---
-
--- confirm required permissions using audit messages
--- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
-SET sepgsql.debug_audit = true;
-SET client_min_messages = LOG;
-
-CREATE DATABASE regtest_sepgsql_test_database;
-
-CREATE SCHEMA regtest_schema;
-
-SET search_path = regtest_schema, public;
-
-CREATE TABLE regtest_table (x serial primary key, y text);
-
-ALTER TABLE regtest_table ADD COLUMN z int;
-
-CREATE TABLE regtest_table_2 (a int) WITH OIDS;
-
--- corresponding toast table should not have label and permission checks
-ALTER TABLE regtest_table_2 ADD COLUMN b text;
-
--- VACUUM FULL internally create a new table and swap them later.
-VACUUM FULL regtest_table;
-
-CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
-
-CREATE SEQUENCE regtest_seq;
-
-CREATE TYPE regtest_comptype AS (a int, b text);
-
-CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
- AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
-
-CREATE AGGREGATE regtest_agg (
- sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
-);
-
---
--- clean-up
---
-DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
-
-DROP SCHEMA IF EXISTS regtest_schema CASCADE;
diff --git a/contrib/sepgsql/sql/ddl.sql b/contrib/sepgsql/sql/ddl.sql
new file mode 100644
index 0000000..8dd57e0
--- /dev/null
+++ b/contrib/sepgsql/sql/ddl.sql
@@ -0,0 +1,81 @@
+--
+-- Regression Test for DDL of Object Permission Checks
+--
+
+-- confirm required permissions using audit messages
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+
+--
+-- CREATE Permission checks
+--
+CREATE DATABASE regtest_sepgsql_test_database;
+
+CREATE USER regtest_sepgsql_test_user;
+
+CREATE SCHEMA regtest_schema;
+
+GRANT ALL ON SCHEMA regtest_schema TO regtest_sepgsql_test_user;
+
+SET search_path = regtest_schema, public;
+
+CREATE TABLE regtest_table (x serial primary key, y text);
+
+ALTER TABLE regtest_table ADD COLUMN z int;
+
+CREATE TABLE regtest_table_2 (a int) WITH OIDS;
+
+-- corresponding toast table should not have label and permission checks
+ALTER TABLE regtest_table_2 ADD COLUMN b text;
+
+-- VACUUM FULL internally create a new table and swap them later.
+VACUUM FULL regtest_table;
+
+CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
+
+CREATE SEQUENCE regtest_seq;
+
+CREATE TYPE regtest_comptype AS (a int, b text);
+
+CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
+
+CREATE AGGREGATE regtest_agg (
+ sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
+);
+
+-- CREATE objects owned by others
+SET SESSION AUTHORIZATION regtest_sepgsql_test_user;
+
+SET search_path = regtest_schema, public;
+
+CREATE TABLE regtest_table_3 (x int, y serial);
+
+CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < y;
+
+CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RETURN $1 * $1 < 100; END';
+
+RESET SESSION AUTHORIZATION;
+
+--
+-- DROP Permission checks (with clean-up)
+--
+
+DROP FUNCTION regtest_func(text,int[]);
+DROP AGGREGATE regtest_agg(int);
+
+DROP SEQUENCE regtest_seq;
+DROP VIEW regtest_view;
+
+ALTER TABLE regtest_table DROP COLUMN y;
+ALTER TABLE regtest_table_2 SET WITHOUT OIDS;
+
+DROP TABLE regtest_table;
+
+DROP OWNED BY regtest_sepgsql_test_user;
+
+DROP DATABASE regtest_sepgsql_test_database;
+DROP USER regtest_sepgsql_test_user;
+DROP SCHEMA IF EXISTS regtest_schema CASCADE;
diff --git a/contrib/sepgsql/test_sepgsql b/contrib/sepgsql/test_sepgsql
index 52237e6..473004f 100755
--- a/contrib/sepgsql/test_sepgsql
+++ b/contrib/sepgsql/test_sepgsql
@@ -259,6 +259,6 @@ echo "found ${NUM}"
echo
echo "============== running sepgsql regression tests =============="
-make REGRESS="label dml create misc" REGRESS_OPTS="--launcher ./launcher" installcheck
+make REGRESS="label dml ddl misc" REGRESS_OPTS="--launcher ./launcher" installcheck
# exit with the exit code provided by "make"
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index e45c258..d9b1fed 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -448,6 +448,20 @@ UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
</para>
<para>
+ When <literal>DROP</> command is executed, <literal>drop</> will be
+ checked on the object being removed for each object types.
+ Please note that it shall not be checked on the objects removed by
+ cascaded deletion according to the standard manner in SQL.
+ </para>
+ <para>
+ A few additional checks are applied depending on object types.
+ On deletion of objects underlying a particula schema (tables, views,
+ sequences and procedures), <literal>remove_name</> shall be also checked
+ on the schema, not only <literal>drop</> on the object being removed
+ itself.
+ </para>
+
+ <para>
When <xref linkend="sql-security-label"> is executed, <literal>setattr</>
and <literal>relabelfrom</> will be checked on the object being relabeled
with its old security label, then <literal>relabelto</> with the supplied
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index c3e55ab..1ed2d70 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -20,6 +20,7 @@
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_conversion.h"
@@ -1232,6 +1233,10 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
obj.objectId = sdepForm->objid;
obj.objectSubId = sdepForm->objsubid;
add_exact_object_address(&obj, deleteobjs);
+
+ /* DROP hook for the object being removed */
+ InvokeObjectAccessHook(OAT_DROP, obj.classId,
+ obj.objectId, obj.objectSubId);
break;
}
}
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 42a8b31..ca12369 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -777,6 +777,9 @@ dropdb(const char *dbname, bool missing_ok)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
dbname);
+ /* DROP hook for the database being removed */
+ InvokeObjectAccessHook(OAT_DROP, DatabaseRelationId, db_id, 0);
+
/*
* Disallow dropping a DB that is marked istemplate. This is just to
* prevent people from accidentally dropping template0 or template1; they
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 9eeba04..c52310e 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_class.h"
#include "catalog/pg_proc.h"
@@ -111,6 +112,10 @@ RemoveObjects(DropStmt *stmt)
check_object_ownership(GetUserId(), stmt->removeType, address,
objname, objargs, relation);
+ /* DROP hook for the object being removed */
+ InvokeObjectAccessHook(OAT_DROP, address.classId,
+ address.objectId, address.objectSubId);
+
/* Release any relcache reference count, but keep lock until commit. */
if (relation)
heap_close(relation, NoLock);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c373016..8a7f938 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -806,6 +806,9 @@ RemoveRelations(DropStmt *drop)
continue;
}
+ /* DROP hook for the relation being removed */
+ InvokeObjectAccessHook(OAT_DROP, RelationRelationId, relOid, 0);
+
/* OK, we're ready to delete this one */
obj.classId = RelationRelationId;
obj.objectId = relOid;
@@ -5088,6 +5091,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
attnum = targetatt->attnum;
+ /* DROP hook for the attribute being removed */
+ InvokeObjectAccessHook(OAT_DROP, RelationRelationId,
+ RelationGetRelid(rel), attnum);
+
/* Can't drop a system attribute, except OID */
if (attnum <= 0 && attnum != ObjectIdAttributeNumber)
ereport(ERROR,
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index ff58490..bf9f362 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -434,6 +434,9 @@ DropTableSpace(DropTableSpaceStmt *stmt)
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE,
tablespacename);
+ /* DROP hook for the tablespace being removed */
+ InvokeObjectAccessHook(OAT_DROP, TableSpaceRelationId, tablespaceoid, 0);
+
/*
* Remove the pg_tablespace tuple (this will roll back if we fail below)
*/
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 5c7a40a..cb38cba 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -19,11 +19,15 @@
* Typically, this is done after inserting the primary catalog records and
* associated dependencies.
*
+ * OAT_DROP should be invoked just after the permission checks of objects
+ * deletion. Please note that it shall NOT be invoked around cascaded drops.
+ *
* Other types may be added in the future.
*/
typedef enum ObjectAccessType
{
OAT_POST_CREATE,
+ OAT_DROP,
} ObjectAccessType;
/*
On Tue, Jan 10, 2012 at 7:51 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
The attached patch adds OAT_DROP object-access-hook around permission
checks of object deletion.
Due to the previous drop statement reworks, the number of places to
put this hook is limited to these six points: RemoveObjects,
RemoveRelations, ATExecDropColumn, dropdb, DropTableSpace and
shdepDropOwned().In sepgsql side, it checks {drop} permission for each object types,
and {remove_name} permission to the schema that owning the object
being removed. I'm still considering whether the drop permission
should be applied on objects being removed in cascade mode.
It is not difficult to implement. We can determine the bahavior on
object deletion with same manner of creation; that saves contextual
information using ProcessUtility hook.At this moment, the current proposed patch does not apply checks on
cascaded deletion, according to SQL behavior. However, my concern is
that user can automatically have right to remove all the objects
underlying a partidular schema being removable, even if individual
tables or functions are not able to be removed.So, my preference is sepgsql references dependency tables to check
{drop} permissions of involved objects, not only the target object.
Hmm. I kind of wonder if we shouldn't just have the OAT_DROP hook get
invoked for every actual drop, rather than only for the top-level
object. It's somewhat appealing to have the hook more-or-less match
up the permissions checks, as you have it here, but in general it
seems more useful to have a callback for each thing dropped than to
have a callback for each thing named to be dropped. What is your
opinion?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2012/1/17 Robert Haas <robertmhaas@gmail.com>:
On Tue, Jan 10, 2012 at 7:51 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
The attached patch adds OAT_DROP object-access-hook around permission
checks of object deletion.
Due to the previous drop statement reworks, the number of places to
put this hook is limited to these six points: RemoveObjects,
RemoveRelations, ATExecDropColumn, dropdb, DropTableSpace and
shdepDropOwned().In sepgsql side, it checks {drop} permission for each object types,
and {remove_name} permission to the schema that owning the object
being removed. I'm still considering whether the drop permission
should be applied on objects being removed in cascade mode.
It is not difficult to implement. We can determine the bahavior on
object deletion with same manner of creation; that saves contextual
information using ProcessUtility hook.At this moment, the current proposed patch does not apply checks on
cascaded deletion, according to SQL behavior. However, my concern is
that user can automatically have right to remove all the objects
underlying a partidular schema being removable, even if individual
tables or functions are not able to be removed.So, my preference is sepgsql references dependency tables to check
{drop} permissions of involved objects, not only the target object.Hmm. I kind of wonder if we shouldn't just have the OAT_DROP hook get
invoked for every actual drop, rather than only for the top-level
object. It's somewhat appealing to have the hook more-or-less match
up the permissions checks, as you have it here, but in general it
seems more useful to have a callback for each thing dropped than to
have a callback for each thing named to be dropped. What is your
opinion?
I think it is more ideal design.
If we could track object deletion only top-level, we have no option to
implement security features based on the object-access-hook, even
if security model is suitable to apply checks all the object deletion.
In addition, I believe it should be used to clean-up something set up
by external modules, not only permission checks.
Do I modify the patch to place object-access-hook on deleteOneObject
(probably, it is the best position to track actual deletion)?
One problem is case of deletion of columns by ALTER TABLE.
It just marks "attisdropped" flag; without removing catalog entry.
Do we ought to put this hook on ATExecDropColumn exceptionally?
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
On Tue, Jan 17, 2012 at 10:55 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
Do I modify the patch to place object-access-hook on deleteOneObject
(probably, it is the best position to track actual deletion)?
One problem is case of deletion of columns by ALTER TABLE.
It just marks "attisdropped" flag; without removing catalog entry.
Do we ought to put this hook on ATExecDropColumn exceptionally?
+1.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2012/1/18 Robert Haas <robertmhaas@gmail.com>:
On Tue, Jan 17, 2012 at 10:55 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
Do I modify the patch to place object-access-hook on deleteOneObject
(probably, it is the best position to track actual deletion)?
One problem is case of deletion of columns by ALTER TABLE.
It just marks "attisdropped" flag; without removing catalog entry.
Do we ought to put this hook on ATExecDropColumn exceptionally?+1.
The attached one is a revised version.
It adds OAT_DROP on the following points:
* deleteOneObject()
* dropdb()
* DropTableSpace()
* DropRole()
One thing I overlooked on the upthread is that ATExecDropColumn()
also calls performDeletion, then RemoveAttributeById() set attisdropped.
So, this function was not a point to be hooked exceptionally.
On the other hand, as a source code comment on doDeletion() says,
deletion of shared database objects (database, tablespace and role)
are not hooked on deleteOneObject(), thus, I added a hook for each
deletion code of these objects.
In sepgsql side, it determines a case to apply permission checks
according to the contextual information; that is same technique
when we implemented create permission.
Thus, it could checks db_xxx:{drop} permission correctly.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
Attachments:
pgsql-v9.2-drop-permissions.v2.patchapplication/octet-stream; name=pgsql-v9.2-drop-permissions.v2.patchDownload
diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c
index be3a7be..0c395c4 100644
--- a/contrib/sepgsql/database.c
+++ b/contrib/sepgsql/database.c
@@ -119,6 +119,33 @@ sepgsql_database_post_create(Oid databaseId, const char *dtemplate)
}
/*
+ * sepgsql_database_drop
+ *
+ * It checks privileges to drop the supplied database
+ */
+void
+sepgsql_database_drop(Oid databaseId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_database:{drop} permission
+ */
+ object.classId = DatabaseRelationId;
+ object.objectId = databaseId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_DATABASE,
+ SEPG_DB_DATABASE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_database_relabel
*
* It checks privileges to relabel the supplied database with the `seclabel'
diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out
deleted file mode 100644
index 0f04a3e..0000000
--- a/contrib/sepgsql/expected/create.out
+++ /dev/null
@@ -1,80 +0,0 @@
---
--- Regression Test for Creation of Object Permission Checks
---
--- confirm required permissions using audit messages
-SELECT sepgsql_getcon(); -- confirm client privilege
- sepgsql_getcon
--------------------------------------------
- unconfined_u:unconfined_r:unconfined_t:s0
-(1 row)
-
-SET sepgsql.debug_audit = true;
-SET client_min_messages = LOG;
-CREATE DATABASE regtest_sepgsql_test_database;
-LOG: SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
-LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
-CREATE SCHEMA regtest_schema;
-LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
-SET search_path = regtest_schema, public;
-CREATE TABLE regtest_table (x serial primary key, y text);
-NOTICE: CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x"
-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_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq"
-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"
-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 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 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 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 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 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 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"
-NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for 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;
-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_2"
-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_2 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_2 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_2 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_2 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_2 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_2 column oid"
-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_2 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_2 column a"
--- corresponding toast table should not have label and permission checks
-ALTER TABLE regtest_table_2 ADD COLUMN b text;
-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_2 column b"
--- VACUUM FULL internally create a new table and swap them later.
-VACUUM FULL regtest_table;
-CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
-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_view_t:s0 tclass=db_view name="view regtest_view"
-CREATE SEQUENCE regtest_seq;
-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_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
-CREATE TYPE regtest_comptype AS (a int, b text);
-CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
- AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
-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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
-CREATE AGGREGATE regtest_agg (
- sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
-);
-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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
---
--- clean-up
---
-DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
-DROP SCHEMA IF EXISTS regtest_schema CASCADE;
-NOTICE: drop cascades to 7 other objects
-DETAIL: drop cascades to table regtest_table
-drop cascades to table regtest_table_2
-drop cascades to view regtest_view
-drop cascades to sequence regtest_seq
-drop cascades to type regtest_comptype
-drop cascades to function regtest_func(text,integer[])
-drop cascades to function regtest_agg(integer)
diff --git a/contrib/sepgsql/expected/ddl.out b/contrib/sepgsql/expected/ddl.out
new file mode 100644
index 0000000..1c7bcc5
--- /dev/null
+++ b/contrib/sepgsql/expected/ddl.out
@@ -0,0 +1,164 @@
+--
+-- Regression Test for DDL of Object Permission Checks
+--
+-- confirm required permissions using audit messages
+SELECT sepgsql_getcon(); -- confirm client privilege
+ sepgsql_getcon
+-------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0
+(1 row)
+
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+--
+-- CREATE Permission checks
+--
+CREATE DATABASE regtest_sepgsql_test_database;
+LOG: SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
+LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
+CREATE USER regtest_sepgsql_test_user;
+CREATE SCHEMA regtest_schema;
+LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+GRANT ALL ON SCHEMA regtest_schema TO regtest_sepgsql_test_user;
+SET search_path = regtest_schema, public;
+CREATE TABLE regtest_table (x serial primary key, y text);
+NOTICE: CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x"
+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_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq"
+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"
+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 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 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 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 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 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 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"
+NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for 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;
+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_2"
+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_2 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_2 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_2 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_2 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_2 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_2 column oid"
+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_2 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_2 column a"
+-- corresponding toast table should not have label and permission checks
+ALTER TABLE regtest_table_2 ADD COLUMN b text;
+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_2 column b"
+-- VACUUM FULL internally create a new table and swap them later.
+VACUUM FULL regtest_table;
+CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
+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_view_t:s0 tclass=db_view name="view regtest_view"
+CREATE SEQUENCE regtest_seq;
+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_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
+CREATE TYPE regtest_comptype AS (a int, b text);
+CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
+CREATE AGGREGATE regtest_agg (
+ sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
+);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
+-- CREATE objects owned by others
+SET SESSION AUTHORIZATION regtest_sepgsql_test_user;
+SET search_path = regtest_schema, public;
+CREATE TABLE regtest_table_3 (x int, y serial);
+NOTICE: CREATE TABLE will create implicit sequence "regtest_table_3_y_seq" for serial column "regtest_table_3.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 { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_3_y_seq"
+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_3"
+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_3 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_3 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_3 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_3 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_3 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_3 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_3 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_3 column y"
+CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < 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 { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view_2"
+CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RETURN $1 * $1 < 100; END';
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_2(integer)"
+RESET SESSION AUTHORIZATION;
+--
+-- DROP Permission checks (with clean-up)
+--
+DROP FUNCTION regtest_func(text,int[]);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
+DROP AGGREGATE regtest_agg(int);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
+DROP SEQUENCE regtest_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 { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
+DROP VIEW regtest_view;
+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_view_t:s0 tclass=db_view name="view regtest_view"
+ALTER TABLE regtest_table DROP 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 column y"
+ALTER TABLE regtest_table_2 SET WITHOUT OIDS;
+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_2 column oid"
+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 { 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"
+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 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 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 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 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 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 column z"
+DROP OWNED BY regtest_sepgsql_test_user;
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_2(integer)"
+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_view_t:s0 tclass=db_view name="view regtest_view_2"
+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_3_y_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 { 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_3"
+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_3 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_3 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_3 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_3 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_3 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_3 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_3 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_3 column y"
+DROP DATABASE regtest_sepgsql_test_database;
+LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
+DROP USER regtest_sepgsql_test_user;
+DROP SCHEMA IF EXISTS regtest_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table regtest_table_2
+drop cascades to type regtest_comptype
+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_2"
+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_2 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_2 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_2 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_2 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_2 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_2 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_2 column a"
+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_2 column b"
+LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 47437ba..8a38891 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -192,6 +192,46 @@ sepgsql_object_access(ObjectAccessType access,
}
break;
+ case OAT_DROP:
+ switch (classId)
+ {
+ case DatabaseRelationId:
+ if (sepgsql_context_info.cmdtype == T_DropdbStmt)
+ sepgsql_database_drop(objectId);
+ break;
+
+ case NamespaceRelationId:
+ if (sepgsql_context_info.cmdtype == T_DropStmt ||
+ sepgsql_context_info.cmdtype == T_DropOwnedStmt)
+ sepgsql_schema_drop(objectId);
+ break;
+
+ case RelationRelationId:
+ if (subId == 0)
+ {
+ if (sepgsql_context_info.cmdtype == T_DropStmt ||
+ sepgsql_context_info.cmdtype == T_DropOwnedStmt)
+ sepgsql_relation_drop(objectId);
+ }
+ else
+ {
+ if (sepgsql_context_info.cmdtype == T_AlterTableStmt)
+ sepgsql_attribute_drop(objectId, subId);
+ }
+ break;
+
+ case ProcedureRelationId:
+ if (sepgsql_context_info.cmdtype == T_DropStmt ||
+ sepgsql_context_info.cmdtype == T_DropOwnedStmt)
+ sepgsql_proc_drop(objectId);
+ break;
+
+ default:
+ /* Ignore unsupported object classes */
+ break;
+ }
+ break;
+
default:
elog(ERROR, "unexpected object access type: %d", (int) access);
break;
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
index b902797..1efbc90 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -131,6 +131,48 @@ sepgsql_proc_post_create(Oid functionId)
}
/*
+ * sepgsql_proc_drop
+ *
+ * It checks privileges to drop the supplied function.
+ */
+void
+sepgsql_proc_drop(Oid functionId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_schema:{remove_name} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = get_func_namespace(functionId);
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__REMOVE_NAME,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_procedure:{drop} permission
+ */
+ object.classId = ProcedureRelationId;
+ object.objectId = functionId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_DB_PROCEDURE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_proc_relabel
*
* It checks privileges to relabel the supplied function
diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c
index efce914..259be49 100644
--- a/contrib/sepgsql/relation.c
+++ b/contrib/sepgsql/relation.c
@@ -21,6 +21,7 @@
#include "commands/seclabel.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/tqual.h"
#include "sepgsql.h"
@@ -110,6 +111,36 @@ sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
}
/*
+ * sepgsql_attribute_drop
+ *
+ * It checks privileges to drop the supplied column.
+ */
+void
+sepgsql_attribute_drop(Oid relOid, AttrNumber attnum)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ if (get_rel_relkind(relOid) != RELKIND_RELATION)
+ return;
+
+ /*
+ * check db_column:{drop} permission
+ */
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attnum;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_DB_COLUMN__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_attribute_relabel
*
* It checks privileges to relabel the supplied column
@@ -310,6 +341,94 @@ out:
}
/*
+ * sepgsql_relation_drop
+ *
+ * It checks privileges to drop the supplied relation.
+ */
+void
+sepgsql_relation_drop(Oid relOid)
+{
+ ObjectAddress object;
+ char *audit_name;
+ uint16_t tclass = 0;
+ 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;
+
+ /*
+ * check db_schema:{remove_name} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = get_rel_namespace(relOid);
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__REMOVE_NAME,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_table/sequence/view:{drop} permission
+ */
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ tclass,
+ SEPG_DB_TABLE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_column:{drop} permission
+ */
+ if (relkind == RELKIND_RELATION)
+ {
+ Form_pg_attribute attForm;
+ CatCList *attrList;
+ HeapTuple atttup;
+ int i;
+
+ attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid));
+ for (i=0; i < attrList->n_members; i++)
+ {
+ atttup = &attrList->members[i]->tuple;
+ attForm = (Form_pg_attribute) GETSTRUCT(atttup);
+
+ if (attForm->attisdropped)
+ continue;
+
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attForm->attnum;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_DB_COLUMN__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+ }
+ ReleaseCatCacheList(attrList);
+ }
+}
+
+/*
* sepgsql_relation_relabel
*
* It checks privileges to relabel the supplied relation by the `seclabel'.
diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c
index 90dca1d..31d60ef 100644
--- a/contrib/sepgsql/schema.c
+++ b/contrib/sepgsql/schema.c
@@ -97,6 +97,33 @@ sepgsql_schema_post_create(Oid namespaceId)
}
/*
+ * sepgsql_schema_drop
+ *
+ * It checks privileges to drop the supplied schema object.
+ */
+void
+sepgsql_schema_drop(Oid namespaceId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_schema:{drop} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = namespaceId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_schema_relabel
*
* It checks privileges to relabel the supplied schema
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index c93da7a..a7a4532 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -288,27 +288,32 @@ extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort);
*/
extern void sepgsql_database_post_create(Oid databaseId,
const char *dtemplate);
+extern void sepgsql_database_drop(Oid databaseId);
extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel);
/*
* schema.c
*/
extern void sepgsql_schema_post_create(Oid namespaceId);
+extern void sepgsql_schema_drop(Oid namespaceId);
extern void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel);
/*
* relation.c
*/
extern void sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum);
+extern void sepgsql_attribute_drop(Oid relOid, AttrNumber attnum);
extern void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
const char *seclabel);
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);
/*
* proc.c
*/
extern void sepgsql_proc_post_create(Oid functionId);
+extern void sepgsql_proc_drop(Oid functionId);
extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel);
#endif /* SEPGSQL_H */
diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql
deleted file mode 100644
index b0695b4..0000000
--- a/contrib/sepgsql/sql/create.sql
+++ /dev/null
@@ -1,46 +0,0 @@
---
--- Regression Test for Creation of Object Permission Checks
---
-
--- confirm required permissions using audit messages
--- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
-SET sepgsql.debug_audit = true;
-SET client_min_messages = LOG;
-
-CREATE DATABASE regtest_sepgsql_test_database;
-
-CREATE SCHEMA regtest_schema;
-
-SET search_path = regtest_schema, public;
-
-CREATE TABLE regtest_table (x serial primary key, y text);
-
-ALTER TABLE regtest_table ADD COLUMN z int;
-
-CREATE TABLE regtest_table_2 (a int) WITH OIDS;
-
--- corresponding toast table should not have label and permission checks
-ALTER TABLE regtest_table_2 ADD COLUMN b text;
-
--- VACUUM FULL internally create a new table and swap them later.
-VACUUM FULL regtest_table;
-
-CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
-
-CREATE SEQUENCE regtest_seq;
-
-CREATE TYPE regtest_comptype AS (a int, b text);
-
-CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
- AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
-
-CREATE AGGREGATE regtest_agg (
- sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
-);
-
---
--- clean-up
---
-DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
-
-DROP SCHEMA IF EXISTS regtest_schema CASCADE;
diff --git a/contrib/sepgsql/sql/ddl.sql b/contrib/sepgsql/sql/ddl.sql
new file mode 100644
index 0000000..8dd57e0
--- /dev/null
+++ b/contrib/sepgsql/sql/ddl.sql
@@ -0,0 +1,81 @@
+--
+-- Regression Test for DDL of Object Permission Checks
+--
+
+-- confirm required permissions using audit messages
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+
+--
+-- CREATE Permission checks
+--
+CREATE DATABASE regtest_sepgsql_test_database;
+
+CREATE USER regtest_sepgsql_test_user;
+
+CREATE SCHEMA regtest_schema;
+
+GRANT ALL ON SCHEMA regtest_schema TO regtest_sepgsql_test_user;
+
+SET search_path = regtest_schema, public;
+
+CREATE TABLE regtest_table (x serial primary key, y text);
+
+ALTER TABLE regtest_table ADD COLUMN z int;
+
+CREATE TABLE regtest_table_2 (a int) WITH OIDS;
+
+-- corresponding toast table should not have label and permission checks
+ALTER TABLE regtest_table_2 ADD COLUMN b text;
+
+-- VACUUM FULL internally create a new table and swap them later.
+VACUUM FULL regtest_table;
+
+CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
+
+CREATE SEQUENCE regtest_seq;
+
+CREATE TYPE regtest_comptype AS (a int, b text);
+
+CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
+
+CREATE AGGREGATE regtest_agg (
+ sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
+);
+
+-- CREATE objects owned by others
+SET SESSION AUTHORIZATION regtest_sepgsql_test_user;
+
+SET search_path = regtest_schema, public;
+
+CREATE TABLE regtest_table_3 (x int, y serial);
+
+CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < y;
+
+CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RETURN $1 * $1 < 100; END';
+
+RESET SESSION AUTHORIZATION;
+
+--
+-- DROP Permission checks (with clean-up)
+--
+
+DROP FUNCTION regtest_func(text,int[]);
+DROP AGGREGATE regtest_agg(int);
+
+DROP SEQUENCE regtest_seq;
+DROP VIEW regtest_view;
+
+ALTER TABLE regtest_table DROP COLUMN y;
+ALTER TABLE regtest_table_2 SET WITHOUT OIDS;
+
+DROP TABLE regtest_table;
+
+DROP OWNED BY regtest_sepgsql_test_user;
+
+DROP DATABASE regtest_sepgsql_test_database;
+DROP USER regtest_sepgsql_test_user;
+DROP SCHEMA IF EXISTS regtest_schema CASCADE;
diff --git a/contrib/sepgsql/test_sepgsql b/contrib/sepgsql/test_sepgsql
index 52237e6..473004f 100755
--- a/contrib/sepgsql/test_sepgsql
+++ b/contrib/sepgsql/test_sepgsql
@@ -259,6 +259,6 @@ echo "found ${NUM}"
echo
echo "============== running sepgsql regression tests =============="
-make REGRESS="label dml create misc" REGRESS_OPTS="--launcher ./launcher" installcheck
+make REGRESS="label dml ddl misc" REGRESS_OPTS="--launcher ./launcher" installcheck
# exit with the exit code provided by "make"
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index e45c258..d9b1fed 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -448,6 +448,20 @@ UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
</para>
<para>
+ When <literal>DROP</> command is executed, <literal>drop</> will be
+ checked on the object being removed for each object types.
+ Please note that it shall not be checked on the objects removed by
+ cascaded deletion according to the standard manner in SQL.
+ </para>
+ <para>
+ A few additional checks are applied depending on object types.
+ On deletion of objects underlying a particula schema (tables, views,
+ sequences and procedures), <literal>remove_name</> shall be also checked
+ on the schema, not only <literal>drop</> on the object being removed
+ itself.
+ </para>
+
+ <para>
When <xref linkend="sql-security-label"> is executed, <literal>setattr</>
and <literal>relabelfrom</> will be checked on the object being relabeled
with its old security label, then <literal>relabelto</> with the supplied
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 0b3d489..12b8c63 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -20,6 +20,7 @@
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
@@ -958,6 +959,12 @@ deleteOneObject(const ObjectAddress *object, Relation depRel)
HeapTuple tup;
/*
+ * DROP hook for the objects being removed.
+ */
+ InvokeObjectAccessHook(OAT_DROP, object->classId,
+ object->objectId, object->objectSubId);
+
+ /*
* 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/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 42a8b31..ca12369 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -777,6 +777,9 @@ dropdb(const char *dbname, bool missing_ok)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
dbname);
+ /* DROP hook for the database being removed */
+ InvokeObjectAccessHook(OAT_DROP, DatabaseRelationId, db_id, 0);
+
/*
* Disallow dropping a DB that is marked istemplate. This is just to
* prevent people from accidentally dropping template0 or template1; they
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index ff58490..bf9f362 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -434,6 +434,9 @@ DropTableSpace(DropTableSpaceStmt *stmt)
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE,
tablespacename);
+ /* DROP hook for the tablespace being removed */
+ InvokeObjectAccessHook(OAT_DROP, TableSpaceRelationId, tablespaceoid, 0);
+
/*
* Remove the pg_tablespace tuple (this will roll back if we fail below)
*/
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 9a88c90..99bf67c 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -947,6 +947,10 @@ DropRole(DropRoleStmt *stmt)
role),
errdetail_internal("%s", detail),
errdetail_log("%s", detail_log)));
+ /*
+ * DROP hook for the role being removed
+ */
+ InvokeObjectAccessHook(OAT_DROP, AuthIdRelationId, roleid, 0);
/*
* Remove the role from the pg_authid table
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 5c7a40a..cb38cba 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -19,11 +19,15 @@
* Typically, this is done after inserting the primary catalog records and
* associated dependencies.
*
+ * OAT_DROP should be invoked just after the permission checks of objects
+ * deletion. Please note that it shall NOT be invoked around cascaded drops.
+ *
* Other types may be added in the future.
*/
typedef enum ObjectAccessType
{
OAT_POST_CREATE,
+ OAT_DROP,
} ObjectAccessType;
/*
On Wed, Jan 18, 2012 at 9:50 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
In sepgsql side, it determines a case to apply permission checks
according to the contextual information; that is same technique
when we implemented create permission.
Thus, it could checks db_xxx:{drop} permission correctly.
Why do we need the contextual information in this case? Why
can't/shouldn't the decision be made solely on the basis of what
object is targeted?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2012/1/19 Robert Haas <robertmhaas@gmail.com>:
On Wed, Jan 18, 2012 at 9:50 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
In sepgsql side, it determines a case to apply permission checks
according to the contextual information; that is same technique
when we implemented create permission.
Thus, it could checks db_xxx:{drop} permission correctly.Why do we need the contextual information in this case? Why
can't/shouldn't the decision be made solely on the basis of what
object is targeted?
Several code-paths to remove a particular objects are not appropriate
to apply permission checks. For example...
[1]: Cleanup of temporary objects on_shmem_eixt() registers RemoveTempRelationsCallback(), then it eventually calls deleteWhatDependsOn() that have an invocation of deleteOneObject(). This code path is just an internal cleanup process, not related to permission of the client.
on_shmem_eixt() registers RemoveTempRelationsCallback(), then
it eventually calls deleteWhatDependsOn() that have an invocation
of deleteOneObject().
This code path is just an internal cleanup process, not related to
permission of the client.
[2]: Cleanup of transient table at VACUUM FULL/CLUSTER command rebuild_relation() creates a temporary table with make_new_heap(), then it copies the contents of original table according to the order of index, and calls finish_heap_swap() that swaps relation files and removes the temporary table using performDeletion(). This code actually create and drop a table, however, it is quite internal design and not related to permission of the client.
rebuild_relation() creates a temporary table with make_new_heap(),
then it copies the contents of original table according to the order of
index, and calls finish_heap_swap() that swaps relation files and
removes the temporary table using performDeletion().
This code actually create and drop a table, however, it is quite
internal design and not related to permission of the client.
So, I think sepgsql should only applied to permission checks
object deletion invoked by user's operations, such as DropStmt.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
On Thu, Jan 19, 2012 at 3:51 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
2012/1/19 Robert Haas <robertmhaas@gmail.com>:
On Wed, Jan 18, 2012 at 9:50 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
In sepgsql side, it determines a case to apply permission checks
according to the contextual information; that is same technique
when we implemented create permission.
Thus, it could checks db_xxx:{drop} permission correctly.Why do we need the contextual information in this case? Why
can't/shouldn't the decision be made solely on the basis of what
object is targeted?Several code-paths to remove a particular objects are not appropriate
to apply permission checks. For example...[1] Cleanup of temporary objects
on_shmem_eixt() registers RemoveTempRelationsCallback(), then
it eventually calls deleteWhatDependsOn() that have an invocation
of deleteOneObject().
This code path is just an internal cleanup process, not related to
permission of the client.[2] Cleanup of transient table at VACUUM FULL/CLUSTER command
rebuild_relation() creates a temporary table with make_new_heap(),
then it copies the contents of original table according to the order of
index, and calls finish_heap_swap() that swaps relation files and
removes the temporary table using performDeletion().
This code actually create and drop a table, however, it is quite
internal design and not related to permission of the client.So, I think sepgsql should only applied to permission checks
object deletion invoked by user's operations, such as DropStmt.
I agree with that theory, but isn't this method of implementing that a
pretty horrible kludge? For example, if you'd implemented it this way
for 9.1, the recent drop-statement refactoring would have broken it.
Or if, in the future, we add another type of statement that can drop
things, this code will still compile just fine but will no longer work
correctly. ISTM that we need a way to either (1) not call the hook at
all unless the operation is user-initiated, or (2) call the hook, but
pass a flag indicating what sort of operation this is?
Let's imagine another possible use of this hook: we want to emit some
kind of log message every time a database object gets dropped. I
think that's a plausible use-case, and in that case what we'd want is:
(1) VACUUM FULL or CLUSTER shouldn't call the hook at all, (2) cleanup
of temporary objects should probably call the hook, but ideally with a
flag to indicate that it's an internal (DB-initiated) operation, and
(3) user activity should definitely call the hook.
I'm not sure how we can cleanly get that behavior, but ISTM that's
what we want...
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2012/1/19 Robert Haas <robertmhaas@gmail.com>:
On Thu, Jan 19, 2012 at 3:51 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
2012/1/19 Robert Haas <robertmhaas@gmail.com>:
On Wed, Jan 18, 2012 at 9:50 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
In sepgsql side, it determines a case to apply permission checks
according to the contextual information; that is same technique
when we implemented create permission.
Thus, it could checks db_xxx:{drop} permission correctly.Why do we need the contextual information in this case? Why
can't/shouldn't the decision be made solely on the basis of what
object is targeted?Several code-paths to remove a particular objects are not appropriate
to apply permission checks. For example...[1] Cleanup of temporary objects
on_shmem_eixt() registers RemoveTempRelationsCallback(), then
it eventually calls deleteWhatDependsOn() that have an invocation
of deleteOneObject().
This code path is just an internal cleanup process, not related to
permission of the client.[2] Cleanup of transient table at VACUUM FULL/CLUSTER command
rebuild_relation() creates a temporary table with make_new_heap(),
then it copies the contents of original table according to the order of
index, and calls finish_heap_swap() that swaps relation files and
removes the temporary table using performDeletion().
This code actually create and drop a table, however, it is quite
internal design and not related to permission of the client.So, I think sepgsql should only applied to permission checks
object deletion invoked by user's operations, such as DropStmt.I agree with that theory, but isn't this method of implementing that a
pretty horrible kludge? For example, if you'd implemented it this way
for 9.1, the recent drop-statement refactoring would have broken it.
Or if, in the future, we add another type of statement that can drop
things, this code will still compile just fine but will no longer work
correctly. ISTM that we need a way to either (1) not call the hook at
all unless the operation is user-initiated, or (2) call the hook, but
pass a flag indicating what sort of operation this is?
Yes. This approach requires to continue code revising on sepgsql-side
also for each major release.
I think the approach (1) raise an issue similar to what we discussed
when sepgsql implemented create permission; we have to know
details of extension module to determine whether the hook should
be called, or not. My preference is (2) that is more reasonable.
Let's imagine another possible use of this hook: we want to emit some
kind of log message every time a database object gets dropped. I
think that's a plausible use-case, and in that case what we'd want is:
(1) VACUUM FULL or CLUSTER shouldn't call the hook at all, (2) cleanup
of temporary objects should probably call the hook, but ideally with a
flag to indicate that it's an internal (DB-initiated) operation, and
(3) user activity should definitely call the hook.I'm not sure how we can cleanly get that behavior, but ISTM that's
what we want...
I think the case (1) should also call the hook but with a flag that indicate
database internal stuff, because make_new_heap() calls OAT_POST_CREATE
hook, thus, we need to give extensions a chance to cleanup, if it did
something on this timing.
I'd like to propose to utilize DropBehavior argument of performDeletion()
to inform dependency.c its invoked context.
If we have a new flag DROP_INTERNAL that can be OR-masked with
existing DROP_RESTRICT or DROP_CASCADE, it can be used to
inform the current context, then, it shall be used to the flag to the hook.
It seems to me this approach is minimum invasive.
In addition, we may have a case when extension want to know whether
the deletion is cascaded, or explicitly specified by users. If they want to
implement same security model on this hook.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
I tried to implement based on the idea to call object-access-hook with
flag; that
informs extensions contexts of objects being removed.
Because I missed DROP_RESTRICT and DROP_CASCADE are enum type,
this patch added performInternalDeletion() instead of OR-masked DROP_INTERNAL.
All its difference from performDeletion() is a flag (OAT_DROP_FLAGS_INTERNAL)
shall be delivered to extension module. I replaced several performDeletion() by
performInternalDeletion() that clean-up objects due to internal stuff.
How about this approach?
Thanks,
2012/1/21 Kohei KaiGai <kaigai@kaigai.gr.jp>:
2012/1/19 Robert Haas <robertmhaas@gmail.com>:
On Thu, Jan 19, 2012 at 3:51 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
2012/1/19 Robert Haas <robertmhaas@gmail.com>:
On Wed, Jan 18, 2012 at 9:50 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
In sepgsql side, it determines a case to apply permission checks
according to the contextual information; that is same technique
when we implemented create permission.
Thus, it could checks db_xxx:{drop} permission correctly.Why do we need the contextual information in this case? Why
can't/shouldn't the decision be made solely on the basis of what
object is targeted?Several code-paths to remove a particular objects are not appropriate
to apply permission checks. For example...[1] Cleanup of temporary objects
on_shmem_eixt() registers RemoveTempRelationsCallback(), then
it eventually calls deleteWhatDependsOn() that have an invocation
of deleteOneObject().
This code path is just an internal cleanup process, not related to
permission of the client.[2] Cleanup of transient table at VACUUM FULL/CLUSTER command
rebuild_relation() creates a temporary table with make_new_heap(),
then it copies the contents of original table according to the order of
index, and calls finish_heap_swap() that swaps relation files and
removes the temporary table using performDeletion().
This code actually create and drop a table, however, it is quite
internal design and not related to permission of the client.So, I think sepgsql should only applied to permission checks
object deletion invoked by user's operations, such as DropStmt.I agree with that theory, but isn't this method of implementing that a
pretty horrible kludge? For example, if you'd implemented it this way
for 9.1, the recent drop-statement refactoring would have broken it.
Or if, in the future, we add another type of statement that can drop
things, this code will still compile just fine but will no longer work
correctly. ISTM that we need a way to either (1) not call the hook at
all unless the operation is user-initiated, or (2) call the hook, but
pass a flag indicating what sort of operation this is?Yes. This approach requires to continue code revising on sepgsql-side
also for each major release.
I think the approach (1) raise an issue similar to what we discussed
when sepgsql implemented create permission; we have to know
details of extension module to determine whether the hook should
be called, or not. My preference is (2) that is more reasonable.Let's imagine another possible use of this hook: we want to emit some
kind of log message every time a database object gets dropped. I
think that's a plausible use-case, and in that case what we'd want is:
(1) VACUUM FULL or CLUSTER shouldn't call the hook at all, (2) cleanup
of temporary objects should probably call the hook, but ideally with a
flag to indicate that it's an internal (DB-initiated) operation, and
(3) user activity should definitely call the hook.I'm not sure how we can cleanly get that behavior, but ISTM that's
what we want...I think the case (1) should also call the hook but with a flag that indicate
database internal stuff, because make_new_heap() calls OAT_POST_CREATE
hook, thus, we need to give extensions a chance to cleanup, if it did
something on this timing.I'd like to propose to utilize DropBehavior argument of performDeletion()
to inform dependency.c its invoked context.
If we have a new flag DROP_INTERNAL that can be OR-masked with
existing DROP_RESTRICT or DROP_CASCADE, it can be used to
inform the current context, then, it shall be used to the flag to the hook.
It seems to me this approach is minimum invasive.In addition, we may have a case when extension want to know whether
the deletion is cascaded, or explicitly specified by users. If they want to
implement same security model on this hook.Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
Attachments:
pgsql-v9.2-drop-permissions.v3.patchapplication/octet-stream; name=pgsql-v9.2-drop-permissions.v3.patchDownload
diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c
index be3a7be..0c395c4 100644
--- a/contrib/sepgsql/database.c
+++ b/contrib/sepgsql/database.c
@@ -119,6 +119,33 @@ sepgsql_database_post_create(Oid databaseId, const char *dtemplate)
}
/*
+ * sepgsql_database_drop
+ *
+ * It checks privileges to drop the supplied database
+ */
+void
+sepgsql_database_drop(Oid databaseId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_database:{drop} permission
+ */
+ object.classId = DatabaseRelationId;
+ object.objectId = databaseId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_DATABASE,
+ SEPG_DB_DATABASE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_database_relabel
*
* It checks privileges to relabel the supplied database with the `seclabel'
diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out
deleted file mode 100644
index 0f04a3e..0000000
--- a/contrib/sepgsql/expected/create.out
+++ /dev/null
@@ -1,80 +0,0 @@
---
--- Regression Test for Creation of Object Permission Checks
---
--- confirm required permissions using audit messages
-SELECT sepgsql_getcon(); -- confirm client privilege
- sepgsql_getcon
--------------------------------------------
- unconfined_u:unconfined_r:unconfined_t:s0
-(1 row)
-
-SET sepgsql.debug_audit = true;
-SET client_min_messages = LOG;
-CREATE DATABASE regtest_sepgsql_test_database;
-LOG: SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
-LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
-CREATE SCHEMA regtest_schema;
-LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
-SET search_path = regtest_schema, public;
-CREATE TABLE regtest_table (x serial primary key, y text);
-NOTICE: CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x"
-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_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq"
-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"
-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 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 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 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 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 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 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"
-NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for 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;
-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_2"
-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_2 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_2 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_2 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_2 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_2 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_2 column oid"
-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_2 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_2 column a"
--- corresponding toast table should not have label and permission checks
-ALTER TABLE regtest_table_2 ADD COLUMN b text;
-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_2 column b"
--- VACUUM FULL internally create a new table and swap them later.
-VACUUM FULL regtest_table;
-CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
-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_view_t:s0 tclass=db_view name="view regtest_view"
-CREATE SEQUENCE regtest_seq;
-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_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
-CREATE TYPE regtest_comptype AS (a int, b text);
-CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
- AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
-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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
-CREATE AGGREGATE regtest_agg (
- sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
-);
-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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
---
--- clean-up
---
-DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
-DROP SCHEMA IF EXISTS regtest_schema CASCADE;
-NOTICE: drop cascades to 7 other objects
-DETAIL: drop cascades to table regtest_table
-drop cascades to table regtest_table_2
-drop cascades to view regtest_view
-drop cascades to sequence regtest_seq
-drop cascades to type regtest_comptype
-drop cascades to function regtest_func(text,integer[])
-drop cascades to function regtest_agg(integer)
diff --git a/contrib/sepgsql/expected/ddl.out b/contrib/sepgsql/expected/ddl.out
new file mode 100644
index 0000000..1c7bcc5
--- /dev/null
+++ b/contrib/sepgsql/expected/ddl.out
@@ -0,0 +1,164 @@
+--
+-- Regression Test for DDL of Object Permission Checks
+--
+-- confirm required permissions using audit messages
+SELECT sepgsql_getcon(); -- confirm client privilege
+ sepgsql_getcon
+-------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0
+(1 row)
+
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+--
+-- CREATE Permission checks
+--
+CREATE DATABASE regtest_sepgsql_test_database;
+LOG: SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
+LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
+CREATE USER regtest_sepgsql_test_user;
+CREATE SCHEMA regtest_schema;
+LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+GRANT ALL ON SCHEMA regtest_schema TO regtest_sepgsql_test_user;
+SET search_path = regtest_schema, public;
+CREATE TABLE regtest_table (x serial primary key, y text);
+NOTICE: CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x"
+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_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq"
+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"
+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 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 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 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 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 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 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"
+NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for 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;
+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_2"
+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_2 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_2 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_2 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_2 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_2 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_2 column oid"
+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_2 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_2 column a"
+-- corresponding toast table should not have label and permission checks
+ALTER TABLE regtest_table_2 ADD COLUMN b text;
+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_2 column b"
+-- VACUUM FULL internally create a new table and swap them later.
+VACUUM FULL regtest_table;
+CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
+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_view_t:s0 tclass=db_view name="view regtest_view"
+CREATE SEQUENCE regtest_seq;
+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_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
+CREATE TYPE regtest_comptype AS (a int, b text);
+CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
+CREATE AGGREGATE regtest_agg (
+ sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
+);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
+-- CREATE objects owned by others
+SET SESSION AUTHORIZATION regtest_sepgsql_test_user;
+SET search_path = regtest_schema, public;
+CREATE TABLE regtest_table_3 (x int, y serial);
+NOTICE: CREATE TABLE will create implicit sequence "regtest_table_3_y_seq" for serial column "regtest_table_3.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 { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_3_y_seq"
+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_3"
+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_3 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_3 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_3 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_3 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_3 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_3 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_3 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_3 column y"
+CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < 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 { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view_2"
+CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RETURN $1 * $1 < 100; END';
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_2(integer)"
+RESET SESSION AUTHORIZATION;
+--
+-- DROP Permission checks (with clean-up)
+--
+DROP FUNCTION regtest_func(text,int[]);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
+DROP AGGREGATE regtest_agg(int);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
+DROP SEQUENCE regtest_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 { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
+DROP VIEW regtest_view;
+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_view_t:s0 tclass=db_view name="view regtest_view"
+ALTER TABLE regtest_table DROP 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 column y"
+ALTER TABLE regtest_table_2 SET WITHOUT OIDS;
+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_2 column oid"
+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 { 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"
+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 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 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 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 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 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 column z"
+DROP OWNED BY regtest_sepgsql_test_user;
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_2(integer)"
+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_view_t:s0 tclass=db_view name="view regtest_view_2"
+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_3_y_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 { 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_3"
+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_3 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_3 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_3 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_3 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_3 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_3 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_3 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_3 column y"
+DROP DATABASE regtest_sepgsql_test_database;
+LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
+DROP USER regtest_sepgsql_test_user;
+DROP SCHEMA IF EXISTS regtest_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table regtest_table_2
+drop cascades to type regtest_comptype
+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_2"
+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_2 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_2 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_2 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_2 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_2 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_2 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_2 column a"
+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_2 column b"
+LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 47437ba..cad031d 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -133,10 +133,11 @@ static void
sepgsql_object_access(ObjectAccessType access,
Oid classId,
Oid objectId,
- int subId)
+ int subId,
+ Datum arg)
{
if (next_object_access_hook)
- (*next_object_access_hook) (access, classId, objectId, subId);
+ (*next_object_access_hook) (access, classId, objectId, subId, arg);
switch (access)
{
@@ -192,6 +193,41 @@ sepgsql_object_access(ObjectAccessType access,
}
break;
+ case OAT_DROP:
+ /*
+ * SELinux does not apply permission checks on internal cleanup
+ * of database objects.
+ */
+ if (DatumGetInt32(arg) & OAT_DROP_FLAGS_INTERNAL)
+ break;
+
+ switch (classId)
+ {
+ case DatabaseRelationId:
+ sepgsql_database_drop(objectId);
+ break;
+
+ case NamespaceRelationId:
+ sepgsql_schema_drop(objectId);
+ break;
+
+ case RelationRelationId:
+ if (subId == 0)
+ sepgsql_relation_drop(objectId);
+ else
+ sepgsql_attribute_drop(objectId, subId);
+ break;
+
+ case ProcedureRelationId:
+ sepgsql_proc_drop(objectId);
+ break;
+
+ default:
+ /* Ignore unsupported object classes */
+ break;
+ }
+ break;
+
default:
elog(ERROR, "unexpected object access type: %d", (int) access);
break;
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
index b902797..1efbc90 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -131,6 +131,48 @@ sepgsql_proc_post_create(Oid functionId)
}
/*
+ * sepgsql_proc_drop
+ *
+ * It checks privileges to drop the supplied function.
+ */
+void
+sepgsql_proc_drop(Oid functionId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_schema:{remove_name} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = get_func_namespace(functionId);
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__REMOVE_NAME,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_procedure:{drop} permission
+ */
+ object.classId = ProcedureRelationId;
+ object.objectId = functionId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_DB_PROCEDURE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_proc_relabel
*
* It checks privileges to relabel the supplied function
diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c
index efce914..259be49 100644
--- a/contrib/sepgsql/relation.c
+++ b/contrib/sepgsql/relation.c
@@ -21,6 +21,7 @@
#include "commands/seclabel.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/tqual.h"
#include "sepgsql.h"
@@ -110,6 +111,36 @@ sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
}
/*
+ * sepgsql_attribute_drop
+ *
+ * It checks privileges to drop the supplied column.
+ */
+void
+sepgsql_attribute_drop(Oid relOid, AttrNumber attnum)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ if (get_rel_relkind(relOid) != RELKIND_RELATION)
+ return;
+
+ /*
+ * check db_column:{drop} permission
+ */
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attnum;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_DB_COLUMN__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_attribute_relabel
*
* It checks privileges to relabel the supplied column
@@ -310,6 +341,94 @@ out:
}
/*
+ * sepgsql_relation_drop
+ *
+ * It checks privileges to drop the supplied relation.
+ */
+void
+sepgsql_relation_drop(Oid relOid)
+{
+ ObjectAddress object;
+ char *audit_name;
+ uint16_t tclass = 0;
+ 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;
+
+ /*
+ * check db_schema:{remove_name} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = get_rel_namespace(relOid);
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__REMOVE_NAME,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_table/sequence/view:{drop} permission
+ */
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ tclass,
+ SEPG_DB_TABLE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_column:{drop} permission
+ */
+ if (relkind == RELKIND_RELATION)
+ {
+ Form_pg_attribute attForm;
+ CatCList *attrList;
+ HeapTuple atttup;
+ int i;
+
+ attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid));
+ for (i=0; i < attrList->n_members; i++)
+ {
+ atttup = &attrList->members[i]->tuple;
+ attForm = (Form_pg_attribute) GETSTRUCT(atttup);
+
+ if (attForm->attisdropped)
+ continue;
+
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attForm->attnum;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_DB_COLUMN__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+ }
+ ReleaseCatCacheList(attrList);
+ }
+}
+
+/*
* sepgsql_relation_relabel
*
* It checks privileges to relabel the supplied relation by the `seclabel'.
diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c
index 90dca1d..31d60ef 100644
--- a/contrib/sepgsql/schema.c
+++ b/contrib/sepgsql/schema.c
@@ -97,6 +97,33 @@ sepgsql_schema_post_create(Oid namespaceId)
}
/*
+ * sepgsql_schema_drop
+ *
+ * It checks privileges to drop the supplied schema object.
+ */
+void
+sepgsql_schema_drop(Oid namespaceId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_schema:{drop} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = namespaceId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_schema_relabel
*
* It checks privileges to relabel the supplied schema
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index c93da7a..a7a4532 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -288,27 +288,32 @@ extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort);
*/
extern void sepgsql_database_post_create(Oid databaseId,
const char *dtemplate);
+extern void sepgsql_database_drop(Oid databaseId);
extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel);
/*
* schema.c
*/
extern void sepgsql_schema_post_create(Oid namespaceId);
+extern void sepgsql_schema_drop(Oid namespaceId);
extern void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel);
/*
* relation.c
*/
extern void sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum);
+extern void sepgsql_attribute_drop(Oid relOid, AttrNumber attnum);
extern void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
const char *seclabel);
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);
/*
* proc.c
*/
extern void sepgsql_proc_post_create(Oid functionId);
+extern void sepgsql_proc_drop(Oid functionId);
extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel);
#endif /* SEPGSQL_H */
diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql
deleted file mode 100644
index b0695b4..0000000
--- a/contrib/sepgsql/sql/create.sql
+++ /dev/null
@@ -1,46 +0,0 @@
---
--- Regression Test for Creation of Object Permission Checks
---
-
--- confirm required permissions using audit messages
--- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
-SET sepgsql.debug_audit = true;
-SET client_min_messages = LOG;
-
-CREATE DATABASE regtest_sepgsql_test_database;
-
-CREATE SCHEMA regtest_schema;
-
-SET search_path = regtest_schema, public;
-
-CREATE TABLE regtest_table (x serial primary key, y text);
-
-ALTER TABLE regtest_table ADD COLUMN z int;
-
-CREATE TABLE regtest_table_2 (a int) WITH OIDS;
-
--- corresponding toast table should not have label and permission checks
-ALTER TABLE regtest_table_2 ADD COLUMN b text;
-
--- VACUUM FULL internally create a new table and swap them later.
-VACUUM FULL regtest_table;
-
-CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
-
-CREATE SEQUENCE regtest_seq;
-
-CREATE TYPE regtest_comptype AS (a int, b text);
-
-CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
- AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
-
-CREATE AGGREGATE regtest_agg (
- sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
-);
-
---
--- clean-up
---
-DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
-
-DROP SCHEMA IF EXISTS regtest_schema CASCADE;
diff --git a/contrib/sepgsql/sql/ddl.sql b/contrib/sepgsql/sql/ddl.sql
new file mode 100644
index 0000000..8dd57e0
--- /dev/null
+++ b/contrib/sepgsql/sql/ddl.sql
@@ -0,0 +1,81 @@
+--
+-- Regression Test for DDL of Object Permission Checks
+--
+
+-- confirm required permissions using audit messages
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+
+--
+-- CREATE Permission checks
+--
+CREATE DATABASE regtest_sepgsql_test_database;
+
+CREATE USER regtest_sepgsql_test_user;
+
+CREATE SCHEMA regtest_schema;
+
+GRANT ALL ON SCHEMA regtest_schema TO regtest_sepgsql_test_user;
+
+SET search_path = regtest_schema, public;
+
+CREATE TABLE regtest_table (x serial primary key, y text);
+
+ALTER TABLE regtest_table ADD COLUMN z int;
+
+CREATE TABLE regtest_table_2 (a int) WITH OIDS;
+
+-- corresponding toast table should not have label and permission checks
+ALTER TABLE regtest_table_2 ADD COLUMN b text;
+
+-- VACUUM FULL internally create a new table and swap them later.
+VACUUM FULL regtest_table;
+
+CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
+
+CREATE SEQUENCE regtest_seq;
+
+CREATE TYPE regtest_comptype AS (a int, b text);
+
+CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
+
+CREATE AGGREGATE regtest_agg (
+ sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
+);
+
+-- CREATE objects owned by others
+SET SESSION AUTHORIZATION regtest_sepgsql_test_user;
+
+SET search_path = regtest_schema, public;
+
+CREATE TABLE regtest_table_3 (x int, y serial);
+
+CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < y;
+
+CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RETURN $1 * $1 < 100; END';
+
+RESET SESSION AUTHORIZATION;
+
+--
+-- DROP Permission checks (with clean-up)
+--
+
+DROP FUNCTION regtest_func(text,int[]);
+DROP AGGREGATE regtest_agg(int);
+
+DROP SEQUENCE regtest_seq;
+DROP VIEW regtest_view;
+
+ALTER TABLE regtest_table DROP COLUMN y;
+ALTER TABLE regtest_table_2 SET WITHOUT OIDS;
+
+DROP TABLE regtest_table;
+
+DROP OWNED BY regtest_sepgsql_test_user;
+
+DROP DATABASE regtest_sepgsql_test_database;
+DROP USER regtest_sepgsql_test_user;
+DROP SCHEMA IF EXISTS regtest_schema CASCADE;
diff --git a/contrib/sepgsql/test_sepgsql b/contrib/sepgsql/test_sepgsql
index 52237e6..473004f 100755
--- a/contrib/sepgsql/test_sepgsql
+++ b/contrib/sepgsql/test_sepgsql
@@ -259,6 +259,6 @@ echo "found ${NUM}"
echo
echo "============== running sepgsql regression tests =============="
-make REGRESS="label dml create misc" REGRESS_OPTS="--launcher ./launcher" installcheck
+make REGRESS="label dml ddl misc" REGRESS_OPTS="--launcher ./launcher" installcheck
# exit with the exit code provided by "make"
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index e45c258..d9b1fed 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -448,6 +448,20 @@ UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
</para>
<para>
+ When <literal>DROP</> command is executed, <literal>drop</> will be
+ checked on the object being removed for each object types.
+ Please note that it shall not be checked on the objects removed by
+ cascaded deletion according to the standard manner in SQL.
+ </para>
+ <para>
+ A few additional checks are applied depending on object types.
+ On deletion of objects underlying a particula schema (tables, views,
+ sequences and procedures), <literal>remove_name</> shall be also checked
+ on the schema, not only <literal>drop</> on the object being removed
+ itself.
+ </para>
+
+ <para>
When <xref linkend="sql-security-label"> is executed, <literal>setattr</>
and <literal>relabelfrom</> will be checked on the object being relabeled
with its old security label, then <literal>relabelto</> with the supplied
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 0b3d489..58855dd 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -20,6 +20,7 @@
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
@@ -171,7 +172,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, int32 flags);
static void doDeletion(const ObjectAddress *object);
static void AcquireDeletionLock(const ObjectAddress *object);
static void ReleaseDeletionLock(const ObjectAddress *object);
@@ -201,14 +203,17 @@ static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
* objects, except for those that should be implicitly dropped anyway
* according to the dependency type.
*
+ * performInternalDeletion: same as performDeletion except for a flag
+ * being informed to extension module via object-access-hook.
+ *
* This is the outer control routine for all forms of DROP that drop objects
* that can participate in dependencies. Note that the next two routines
* 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
+execPerformDeletion(const ObjectAddress *object,
+ DropBehavior behavior, bool is_internal)
{
Relation depRel;
ObjectAddresses *targetObjects;
@@ -253,8 +258,15 @@ performDeletion(const ObjectAddress *object,
for (i = 0; i < targetObjects->numrefs; i++)
{
ObjectAddress *thisobj = targetObjects->refs + i;
+ ObjectAddressExtra *thisextra = targetObjects->extras + i;
+ int32 flags = 0;
+
+ if ((thisextra->flags & DEPFLAG_ORIGINAL) == 0)
+ flags |= OAT_DROP_FLAGS_CASCADED;
+ if (is_internal)
+ flags |= OAT_DROP_FLAGS_INTERNAL;
- deleteOneObject(thisobj, depRel);
+ deleteOneObject(thisobj, depRel, flags);
}
/* And clean up */
@@ -263,6 +275,18 @@ performDeletion(const ObjectAddress *object,
heap_close(depRel, RowExclusiveLock);
}
+void
+performDeletion(const ObjectAddress *object, DropBehavior behavior)
+{
+ execPerformDeletion(object, behavior, false);
+}
+
+void
+performInternalDeletion(const ObjectAddress *object, DropBehavior behavior)
+{
+ execPerformDeletion(object, behavior, true);
+}
+
/*
* performMultipleDeletions: Similar to performDeletion, but act on multiple
* objects at once.
@@ -335,8 +359,13 @@ performMultipleDeletions(const ObjectAddresses *objects,
for (i = 0; i < targetObjects->numrefs; i++)
{
ObjectAddress *thisobj = targetObjects->refs + i;
+ ObjectAddressExtra *thisextra = targetObjects->extras + i;
+ int32 flags = 0;
- deleteOneObject(thisobj, depRel);
+ if ((thisextra->flags & DEPFLAG_ORIGINAL) == 0)
+ flags |= OAT_DROP_FLAGS_CASCADED;
+
+ deleteOneObject(thisobj, depRel, flags);
}
/* And clean up */
@@ -407,7 +436,8 @@ deleteWhatDependsOn(const ObjectAddress *object,
if (thisextra->flags & DEPFLAG_ORIGINAL)
continue;
- deleteOneObject(thisobj, depRel);
+ deleteOneObject(thisobj, depRel,
+ OAT_DROP_FLAGS_CASCADED | OAT_DROP_FLAGS_INTERNAL);
}
/* And clean up */
@@ -948,9 +978,10 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
* deleteOneObject: delete a single object for performDeletion.
*
* depRel is the already-open pg_depend relation.
+ * flags is or-masked OAT_DROP_FLAGS_* to be informed to extensions
*/
static void
-deleteOneObject(const ObjectAddress *object, Relation depRel)
+deleteOneObject(const ObjectAddress *object, Relation depRel, int32 flags)
{
ScanKeyData key[3];
int nkeys;
@@ -958,6 +989,13 @@ deleteOneObject(const ObjectAddress *object, Relation depRel)
HeapTuple tup;
/*
+ * DROP hook for the objects being removed.
+ */
+ InvokeObjectAccessHookArg(OAT_DROP, object->classId,
+ object->objectId, object->objectSubId,
+ Int32GetDatum(flags));
+
+ /*
* 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/commands/cluster.c b/src/backend/commands/cluster.c
index 9408f25..8f245c1 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -1443,9 +1443,9 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
* 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);
+ performInternalDeletion(&object, DROP_RESTRICT);
- /* performDeletion does CommandCounterIncrement at end */
+ /* performInternalDeletion does CommandCounterIncrement at end */
/*
* Now we must remove any relation mapping entries that we set up for the
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 42a8b31..ca12369 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -777,6 +777,9 @@ dropdb(const char *dbname, bool missing_ok)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
dbname);
+ /* DROP hook for the database being removed */
+ InvokeObjectAccessHook(OAT_DROP, DatabaseRelationId, db_id, 0);
+
/*
* Disallow dropping a DB that is marked istemplate. This is just to
* prevent people from accidentally dropping template0 or template1; they
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cc210f0..ca7d8e6 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7581,7 +7581,7 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
obj.classId = ConstraintRelationId;
obj.objectId = lfirst_oid(oid_item);
obj.objectSubId = 0;
- performDeletion(&obj, DROP_RESTRICT);
+ performInternalDeletion(&obj, DROP_RESTRICT);
}
foreach(oid_item, tab->changedIndexOids)
@@ -7589,7 +7589,7 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
obj.classId = RelationRelationId;
obj.objectId = lfirst_oid(oid_item);
obj.objectSubId = 0;
- performDeletion(&obj, DROP_RESTRICT);
+ performInternalDeletion(&obj, DROP_RESTRICT);
}
/*
@@ -9738,7 +9738,7 @@ PreCommit_on_commit_actions(void)
object.classId = RelationRelationId;
object.objectId = oc->relid;
object.objectSubId = 0;
- performDeletion(&object, DROP_CASCADE);
+ performInternalDeletion(&object, DROP_CASCADE);
/*
* Note that table deletion will call
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index ff58490..bf9f362 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -434,6 +434,9 @@ DropTableSpace(DropTableSpaceStmt *stmt)
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE,
tablespacename);
+ /* DROP hook for the tablespace being removed */
+ InvokeObjectAccessHook(OAT_DROP, TableSpaceRelationId, tablespaceoid, 0);
+
/*
* Remove the pg_tablespace tuple (this will roll back if we fail below)
*/
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 9a88c90..99bf67c 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -947,6 +947,10 @@ DropRole(DropRoleStmt *stmt)
role),
errdetail_internal("%s", detail),
errdetail_log("%s", detail_log)));
+ /*
+ * DROP hook for the role being removed
+ */
+ InvokeObjectAccessHook(OAT_DROP, AuthIdRelationId, roleid, 0);
/*
* Remove the role from the pg_authid table
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index e84e21c..e13d857 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2044,7 +2044,7 @@ do_autovacuum(void)
object.classId = RelationRelationId;
object.objectId = relid;
object.objectSubId = 0;
- performDeletion(&object, DROP_CASCADE);
+ performInternalDeletion(&object, DROP_CASCADE);
}
else
{
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 087341e..5c44803 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -155,6 +155,9 @@ typedef enum ObjectClass
extern void performDeletion(const ObjectAddress *object,
DropBehavior behavior);
+extern void performInternalDeletion(const ObjectAddress *object,
+ DropBehavior behavior);
+
extern void performMultipleDeletions(const ObjectAddresses *objects,
DropBehavior behavior);
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 5c7a40a..dfbff44 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -19,13 +19,24 @@
* Typically, this is done after inserting the primary catalog records and
* associated dependencies.
*
+ * OAT_DROP should be invoked just before deletion of objects. It takes an
+ * argument to inform extensions contexts of objects being removed.
+ * Also see the comment of OAT_DROP_FLAGS_*.
+ *
* Other types may be added in the future.
*/
typedef enum ObjectAccessType
{
OAT_POST_CREATE,
+ OAT_DROP,
} ObjectAccessType;
+#define OAT_DROP_FLAGS_INTERNAL 0x01 /* this object shall be removed
+ * because of internal clean-up,
+ * not user's activities. */
+#define OAT_DROP_FLAGS_CASCADED 0x02 /* this object shall be removed
+ * because of DROP_CASCADE */
+
/*
* Hook, and a macro to invoke it.
*/
@@ -33,14 +44,22 @@ typedef enum ObjectAccessType
typedef void (*object_access_hook_type) (ObjectAccessType access,
Oid classId,
Oid objectId,
- int subId);
+ int subId,
+ Datum arg);
extern PGDLLIMPORT object_access_hook_type object_access_hook;
#define InvokeObjectAccessHook(access,classId,objectId,subId) \
do { \
if (object_access_hook) \
- (*object_access_hook)((access),(classId),(objectId),(subId)); \
+ (*object_access_hook)((access),(classId),(objectId),(subId),0); \
} while(0)
+#define InvokeObjectAccessHookArg(access,classId,objectId,subId,arg) \
+ do { \
+ if (object_access_hook) \
+ (*object_access_hook)((access),(classId), \
+ (objectId),(subId),(arg)); \
+ } while(0);
+
#endif /* OBJECTACCESS_H */
On Sun, Jan 22, 2012 at 9:54 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
I tried to implement based on the idea to call object-access-hook with
flag; that
informs extensions contexts of objects being removed.
Because I missed DROP_RESTRICT and DROP_CASCADE are enum type,
this patch added performInternalDeletion() instead of OR-masked DROP_INTERNAL.
All its difference from performDeletion() is a flag (OAT_DROP_FLAGS_INTERNAL)
shall be delivered to extension module. I replaced several performDeletion() by
performInternalDeletion() that clean-up objects due to internal stuff.How about this approach?
I generally agree with this line of attack, but I think you've failed
to find all the cases where a drop should be considered internal, and
I'd rather add a new parameter to an existing function than define a
new one that someone might accidentally fail to use in some place
where it's needed. Here's a cut-down patch that *just* adds a
PERFORM_DELETE_INTERNAL flag, plus some related comment additions. If
this looks reasonable to you, I'll commit it and then we can work out
the remaining details.
Since sepgsql doesn't seem to need the DropBehavior, I'm inclined to
say we shouldn't go to any extra work to pass it just now. We can
always add that later if some other client needs it.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Attachments:
perform-deletion-internal.patchapplication/octet-stream; name=perform-deletion-internal.patchDownload
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index f5e6a3f..9315e79 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -1211,7 +1211,7 @@ SetDefaultACL(InternalDefaultACL *iacls)
myself.objectId = HeapTupleGetOid(tuple);
myself.objectSubId = 0;
- performDeletion(&myself, DROP_RESTRICT);
+ performDeletion(&myself, DROP_RESTRICT, 0);
}
}
else
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 0b3d489..db86262 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -171,7 +171,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, int32 flags);
static void doDeletion(const ObjectAddress *object);
static void AcquireDeletionLock(const ObjectAddress *object);
static void ReleaseDeletionLock(const ObjectAddress *object);
@@ -205,10 +206,17 @@ static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
* that can participate in dependencies. Note that the next two routines
* are variants on the same theme; if you change anything here you'll likely
* need to fix them too.
+ *
+ * flags should include PERFORM_DELETION_INTERNAL when the drop operation is
+ * not the direct result of a user-initiated action. For example, when a
+ * temporary schema is cleaned out so that a new backend can use it, or when
+ * a column default is dropped as an intermediate step while adding a new one,
+ * that's an internal operation. On the other hand, when the we drop something
+ * because the user issued a DROP statement against it, that's not internal.
*/
void
performDeletion(const ObjectAddress *object,
- DropBehavior behavior)
+ DropBehavior behavior, int flags)
{
Relation depRel;
ObjectAddresses *targetObjects;
@@ -254,7 +262,7 @@ performDeletion(const ObjectAddress *object,
{
ObjectAddress *thisobj = targetObjects->refs + i;
- deleteOneObject(thisobj, depRel);
+ deleteOneObject(thisobj, depRel, flags);
}
/* And clean up */
@@ -274,7 +282,7 @@ performDeletion(const ObjectAddress *object,
*/
void
performMultipleDeletions(const ObjectAddresses *objects,
- DropBehavior behavior)
+ DropBehavior behavior, int flags)
{
Relation depRel;
ObjectAddresses *targetObjects;
@@ -336,7 +344,7 @@ performMultipleDeletions(const ObjectAddresses *objects,
{
ObjectAddress *thisobj = targetObjects->refs + i;
- deleteOneObject(thisobj, depRel);
+ deleteOneObject(thisobj, depRel, flags);
}
/* And clean up */
@@ -407,7 +415,14 @@ deleteWhatDependsOn(const ObjectAddress *object,
if (thisextra->flags & DEPFLAG_ORIGINAL)
continue;
- deleteOneObject(thisobj, depRel);
+ /*
+ * Since this function is currently only used to clean out temporary
+ * schemas, we pass PERFORM_DELETION_INTERNAL here, indicating that
+ * the operation is an automatic system operation rather than a user
+ * action. If, in the future, this function is used for other
+ * purposes, we might need to revisit this.
+ */
+ deleteOneObject(thisobj, depRel, PERFORM_DELETION_INTERNAL);
}
/* And clean up */
@@ -950,7 +965,7 @@ 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, int flags)
{
ScanKeyData key[3];
int nkeys;
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 204236f..aef410a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1528,7 +1528,7 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
*/
void
RemoveAttrDefault(Oid relid, AttrNumber attnum,
- DropBehavior behavior, bool complain)
+ DropBehavior behavior, bool complain, bool internal)
{
Relation attrdef_rel;
ScanKeyData scankeys[2];
@@ -1559,7 +1559,8 @@ RemoveAttrDefault(Oid relid, AttrNumber attnum,
object.objectId = HeapTupleGetOid(tuple);
object.objectSubId = 0;
- performDeletion(&object, behavior);
+ performDeletion(&object, behavior,
+ internal ? PERFORM_DELETION_INTERNAL : 0);
found = true;
}
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index c3e55ab..11cb988 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -1240,7 +1240,7 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
}
/* the dependency mechanism does the actual work */
- performMultipleDeletions(deleteobjs, behavior);
+ performMultipleDeletions(deleteobjs, behavior, 0);
heap_close(sdepRel, RowExclusiveLock);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 9408f25..349d130 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -1443,7 +1443,7 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
* 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(&object, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
/* performDeletion does CommandCounterIncrement at end */
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 9eeba04..298940c 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -119,7 +119,7 @@ RemoveObjects(DropStmt *stmt)
}
/* Here we really delete them. */
- performMultipleDeletions(objects, stmt->behavior);
+ performMultipleDeletions(objects, stmt->behavior, 0);
free_object_addresses(objects);
}
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index a9ec904..4135e26 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -1286,7 +1286,7 @@ RemoveUserMapping(DropUserMappingStmt *stmt)
object.objectId = umId;
object.objectSubId = 0;
- performDeletion(&object, DROP_CASCADE);
+ performDeletion(&object, DROP_CASCADE, 0);
}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 5dca022..5dc131a 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -1519,7 +1519,7 @@ dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
object.objectId = amopid;
object.objectSubId = 0;
- performDeletion(&object, DROP_RESTRICT);
+ performDeletion(&object, DROP_RESTRICT, 0);
}
}
@@ -1559,7 +1559,7 @@ dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
object.objectId = amprocid;
object.objectSubId = 0;
- performDeletion(&object, DROP_RESTRICT);
+ performDeletion(&object, DROP_RESTRICT, 0);
}
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cb8ac67..9172d99 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -816,7 +816,7 @@ RemoveRelations(DropStmt *drop)
add_exact_object_address(&obj, objects);
}
- performMultipleDeletions(objects, drop->behavior);
+ performMultipleDeletions(objects, drop->behavior, 0);
free_object_addresses(objects);
}
@@ -4803,8 +4803,13 @@ ATExecColumnDefault(Relation rel, const char *colName,
* 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
* default.
+ *
+ * We treat removing the existing default as an internal operation when
+ * it is preparatory to adding a new default, but as a user-initiated
+ * operation when the user asked for a drop.
*/
- RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false);
+ RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
+ newDefault == NULL ? false : true);
if (newDefault)
{
@@ -5217,7 +5222,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
object.objectId = RelationGetRelid(rel);
object.objectSubId = attnum;
- performDeletion(&object, behavior);
+ performDeletion(&object, behavior, 0);
/*
* If we dropped the OID column, must adjust pg_class.relhasoids and tell
@@ -6731,7 +6736,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
conobj.objectId = HeapTupleGetOid(tuple);
conobj.objectSubId = 0;
- performDeletion(&conobj, behavior);
+ performDeletion(&conobj, behavior, 0);
found = true;
@@ -7453,7 +7458,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
* We use RESTRICT here for safety, but at present we do not expect
* anything to depend on the default.
*/
- RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true);
+ RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
+ true);
StoreAttrDefault(rel, attnum, defaultexpr);
}
@@ -7598,7 +7604,7 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
obj.classId = ConstraintRelationId;
obj.objectId = lfirst_oid(oid_item);
obj.objectSubId = 0;
- performDeletion(&obj, DROP_RESTRICT);
+ performDeletion(&obj, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
}
foreach(oid_item, tab->changedIndexOids)
@@ -7606,7 +7612,7 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
obj.classId = RelationRelationId;
obj.objectId = lfirst_oid(oid_item);
obj.objectSubId = 0;
- performDeletion(&obj, DROP_RESTRICT);
+ performDeletion(&obj, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
}
/*
@@ -9764,7 +9770,14 @@ PreCommit_on_commit_actions(void)
object.classId = RelationRelationId;
object.objectId = oc->relid;
object.objectSubId = 0;
- performDeletion(&object, DROP_CASCADE);
+
+ /*
+ * Since this is an automatic drop, rather than one
+ * directly initiated by the user, we pass the
+ * PERFORM_DELETION_INTERNAL flag.
+ */
+ performDeletion(&object,
+ DROP_CASCADE, PERFORM_DELETION_INTERNAL);
/*
* Note that table deletion will call
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 0043bf1..0391848 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2318,7 +2318,7 @@ AlterDomainDropConstraint(List *names, const char *constrName,
conobj.objectId = HeapTupleGetOid(contup);
conobj.objectSubId = 0;
- performDeletion(&conobj, behavior);
+ performDeletion(&conobj, behavior, 0);
found = true;
}
}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index e84e21c..49c15c3 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2044,7 +2044,7 @@ do_autovacuum(void)
object.classId = RelationRelationId;
object.objectId = relid;
object.objectSubId = 0;
- performDeletion(&object, DROP_CASCADE);
+ performDeletion(&object, DROP_CASCADE, PERFORM_DELETION_INTERNAL);
}
else
{
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index afa572b..a14ce44 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -307,7 +307,7 @@ inv_drop(Oid lobjId)
object.classId = LargeObjectRelationId;
object.objectId = lobjId;
object.objectSubId = 0;
- performDeletion(&object, DROP_CASCADE);
+ performDeletion(&object, DROP_CASCADE, 0);
/*
* Advance command counter so that tuple removal will be seen by later
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 087341e..28e68c5 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -152,11 +152,13 @@ typedef enum ObjectClass
/* in dependency.c */
+#define PERFORM_DELETION_INTERNAL 0x0001
+
extern void performDeletion(const ObjectAddress *object,
- DropBehavior behavior);
+ DropBehavior behavior, int flags);
extern void performMultipleDeletions(const ObjectAddresses *objects,
- DropBehavior behavior);
+ DropBehavior behavior, int flags);
extern void deleteWhatDependsOn(const ObjectAddress *object,
bool showNotices);
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 46bee44..2055382 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -107,7 +107,7 @@ extern void DeleteRelationTuple(Oid relid);
extern void DeleteAttributeTuples(Oid relid);
extern void RemoveAttributeById(Oid relid, AttrNumber attnum);
extern void RemoveAttrDefault(Oid relid, AttrNumber attnum,
- DropBehavior behavior, bool complain);
+ DropBehavior behavior, bool complain, bool internal);
extern void RemoveAttrDefaultById(Oid attrdefId);
extern void RemoveStatistics(Oid relid, AttrNumber attnum);
2012/1/25 Robert Haas <robertmhaas@gmail.com>:
On Sun, Jan 22, 2012 at 9:54 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
I tried to implement based on the idea to call object-access-hook with
flag; that
informs extensions contexts of objects being removed.
Because I missed DROP_RESTRICT and DROP_CASCADE are enum type,
this patch added performInternalDeletion() instead of OR-masked DROP_INTERNAL.
All its difference from performDeletion() is a flag (OAT_DROP_FLAGS_INTERNAL)
shall be delivered to extension module. I replaced several performDeletion() by
performInternalDeletion() that clean-up objects due to internal stuff.How about this approach?
I generally agree with this line of attack, but I think you've failed
to find all the cases where a drop should be considered internal, and
I'd rather add a new parameter to an existing function than define a
new one that someone might accidentally fail to use in some place
where it's needed. Here's a cut-down patch that *just* adds a
PERFORM_DELETE_INTERNAL flag, plus some related comment additions. If
this looks reasonable to you, I'll commit it and then we can work out
the remaining details.Since sepgsql doesn't seem to need the DropBehavior, I'm inclined to
say we shouldn't go to any extra work to pass it just now. We can
always add that later if some other client needs it.
It seems to me reasonable design.
The attached patch is rebased one according to your perform-deletion patch.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
Attachments:
pgsql-v9.2-drop-permissions.v4.patchapplication/octet-stream; name=pgsql-v9.2-drop-permissions.v4.patchDownload
contrib/sepgsql/database.c | 27 ++++++
contrib/sepgsql/expected/create.out | 80 -----------------
contrib/sepgsql/expected/ddl.out | 164 +++++++++++++++++++++++++++++++++++
contrib/sepgsql/hooks.c | 40 ++++++++-
contrib/sepgsql/proc.c | 42 +++++++++
contrib/sepgsql/relation.c | 119 +++++++++++++++++++++++++
contrib/sepgsql/schema.c | 27 ++++++
contrib/sepgsql/sepgsql.h | 5 +
contrib/sepgsql/sql/create.sql | 46 ----------
contrib/sepgsql/sql/ddl.sql | 81 +++++++++++++++++
contrib/sepgsql/test_sepgsql | 2 +-
doc/src/sgml/sepgsql.sgml | 14 +++
src/backend/catalog/dependency.c | 6 ++
src/backend/commands/dbcommands.c | 3 +
src/backend/commands/tablespace.c | 3 +
src/backend/commands/user.c | 3 +
src/include/catalog/objectaccess.h | 17 +++-
17 files changed, 548 insertions(+), 131 deletions(-)
diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c
index be3a7be..0c395c4 100644
--- a/contrib/sepgsql/database.c
+++ b/contrib/sepgsql/database.c
@@ -119,6 +119,33 @@ sepgsql_database_post_create(Oid databaseId, const char *dtemplate)
}
/*
+ * sepgsql_database_drop
+ *
+ * It checks privileges to drop the supplied database
+ */
+void
+sepgsql_database_drop(Oid databaseId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_database:{drop} permission
+ */
+ object.classId = DatabaseRelationId;
+ object.objectId = databaseId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_DATABASE,
+ SEPG_DB_DATABASE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_database_relabel
*
* It checks privileges to relabel the supplied database with the `seclabel'
diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out
deleted file mode 100644
index 0f04a3e..0000000
--- a/contrib/sepgsql/expected/create.out
+++ /dev/null
@@ -1,80 +0,0 @@
---
--- Regression Test for Creation of Object Permission Checks
---
--- confirm required permissions using audit messages
-SELECT sepgsql_getcon(); -- confirm client privilege
- sepgsql_getcon
--------------------------------------------
- unconfined_u:unconfined_r:unconfined_t:s0
-(1 row)
-
-SET sepgsql.debug_audit = true;
-SET client_min_messages = LOG;
-CREATE DATABASE regtest_sepgsql_test_database;
-LOG: SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
-LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
-CREATE SCHEMA regtest_schema;
-LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
-SET search_path = regtest_schema, public;
-CREATE TABLE regtest_table (x serial primary key, y text);
-NOTICE: CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x"
-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_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq"
-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"
-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 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 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 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 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 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 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"
-NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for 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;
-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_2"
-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_2 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_2 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_2 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_2 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_2 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_2 column oid"
-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_2 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_2 column a"
--- corresponding toast table should not have label and permission checks
-ALTER TABLE regtest_table_2 ADD COLUMN b text;
-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_2 column b"
--- VACUUM FULL internally create a new table and swap them later.
-VACUUM FULL regtest_table;
-CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
-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_view_t:s0 tclass=db_view name="view regtest_view"
-CREATE SEQUENCE regtest_seq;
-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_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
-CREATE TYPE regtest_comptype AS (a int, b text);
-CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
- AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
-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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
-CREATE AGGREGATE regtest_agg (
- sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
-);
-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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
---
--- clean-up
---
-DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
-DROP SCHEMA IF EXISTS regtest_schema CASCADE;
-NOTICE: drop cascades to 7 other objects
-DETAIL: drop cascades to table regtest_table
-drop cascades to table regtest_table_2
-drop cascades to view regtest_view
-drop cascades to sequence regtest_seq
-drop cascades to type regtest_comptype
-drop cascades to function regtest_func(text,integer[])
-drop cascades to function regtest_agg(integer)
diff --git a/contrib/sepgsql/expected/ddl.out b/contrib/sepgsql/expected/ddl.out
new file mode 100644
index 0000000..1c7bcc5
--- /dev/null
+++ b/contrib/sepgsql/expected/ddl.out
@@ -0,0 +1,164 @@
+--
+-- Regression Test for DDL of Object Permission Checks
+--
+-- confirm required permissions using audit messages
+SELECT sepgsql_getcon(); -- confirm client privilege
+ sepgsql_getcon
+-------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0
+(1 row)
+
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+--
+-- CREATE Permission checks
+--
+CREATE DATABASE regtest_sepgsql_test_database;
+LOG: SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
+LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
+CREATE USER regtest_sepgsql_test_user;
+CREATE SCHEMA regtest_schema;
+LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+GRANT ALL ON SCHEMA regtest_schema TO regtest_sepgsql_test_user;
+SET search_path = regtest_schema, public;
+CREATE TABLE regtest_table (x serial primary key, y text);
+NOTICE: CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x"
+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_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq"
+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"
+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 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 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 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 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 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 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"
+NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for 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;
+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_2"
+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_2 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_2 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_2 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_2 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_2 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_2 column oid"
+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_2 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_2 column a"
+-- corresponding toast table should not have label and permission checks
+ALTER TABLE regtest_table_2 ADD COLUMN b text;
+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_2 column b"
+-- VACUUM FULL internally create a new table and swap them later.
+VACUUM FULL regtest_table;
+CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
+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_view_t:s0 tclass=db_view name="view regtest_view"
+CREATE SEQUENCE regtest_seq;
+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_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
+CREATE TYPE regtest_comptype AS (a int, b text);
+CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
+CREATE AGGREGATE regtest_agg (
+ sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
+);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
+-- CREATE objects owned by others
+SET SESSION AUTHORIZATION regtest_sepgsql_test_user;
+SET search_path = regtest_schema, public;
+CREATE TABLE regtest_table_3 (x int, y serial);
+NOTICE: CREATE TABLE will create implicit sequence "regtest_table_3_y_seq" for serial column "regtest_table_3.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 { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_3_y_seq"
+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_3"
+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_3 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_3 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_3 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_3 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_3 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_3 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_3 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_3 column y"
+CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < 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 { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view_2"
+CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RETURN $1 * $1 < 100; END';
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_2(integer)"
+RESET SESSION AUTHORIZATION;
+--
+-- DROP Permission checks (with clean-up)
+--
+DROP FUNCTION regtest_func(text,int[]);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
+DROP AGGREGATE regtest_agg(int);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
+DROP SEQUENCE regtest_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 { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
+DROP VIEW regtest_view;
+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_view_t:s0 tclass=db_view name="view regtest_view"
+ALTER TABLE regtest_table DROP 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 column y"
+ALTER TABLE regtest_table_2 SET WITHOUT OIDS;
+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_2 column oid"
+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 { 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"
+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 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 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 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 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 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 column z"
+DROP OWNED BY regtest_sepgsql_test_user;
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_2(integer)"
+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_view_t:s0 tclass=db_view name="view regtest_view_2"
+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_3_y_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 { 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_3"
+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_3 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_3 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_3 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_3 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_3 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_3 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_3 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_3 column y"
+DROP DATABASE regtest_sepgsql_test_database;
+LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
+DROP USER regtest_sepgsql_test_user;
+DROP SCHEMA IF EXISTS regtest_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table regtest_table_2
+drop cascades to type regtest_comptype
+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_2"
+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_2 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_2 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_2 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_2 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_2 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_2 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_2 column a"
+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_2 column b"
+LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 47437ba..db70238 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -133,10 +133,11 @@ static void
sepgsql_object_access(ObjectAccessType access,
Oid classId,
Oid objectId,
- int subId)
+ int subId,
+ Datum arg)
{
if (next_object_access_hook)
- (*next_object_access_hook) (access, classId, objectId, subId);
+ (*next_object_access_hook) (access, classId, objectId, subId, arg);
switch (access)
{
@@ -192,6 +193,41 @@ sepgsql_object_access(ObjectAccessType access,
}
break;
+ case OAT_DROP:
+ /*
+ * SELinux does not apply permission checks on internal objects
+ * cleanups; not user's activity.
+ */
+ if (DatumGetInt32(arg) & PERFORM_DELETION_INTERNAL)
+ break;
+
+ switch (classId)
+ {
+ case DatabaseRelationId:
+ sepgsql_database_drop(objectId);
+ break;
+
+ case NamespaceRelationId:
+ sepgsql_schema_drop(objectId);
+ break;
+
+ case RelationRelationId:
+ if (subId == 0)
+ sepgsql_relation_drop(objectId);
+ else
+ sepgsql_attribute_drop(objectId, subId);
+ break;
+
+ case ProcedureRelationId:
+ sepgsql_proc_drop(objectId);
+ break;
+
+ default:
+ /* Ignore unsupported object classes */
+ break;
+ }
+ break;
+
default:
elog(ERROR, "unexpected object access type: %d", (int) access);
break;
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
index b902797..1efbc90 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -131,6 +131,48 @@ sepgsql_proc_post_create(Oid functionId)
}
/*
+ * sepgsql_proc_drop
+ *
+ * It checks privileges to drop the supplied function.
+ */
+void
+sepgsql_proc_drop(Oid functionId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_schema:{remove_name} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = get_func_namespace(functionId);
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__REMOVE_NAME,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_procedure:{drop} permission
+ */
+ object.classId = ProcedureRelationId;
+ object.objectId = functionId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_DB_PROCEDURE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_proc_relabel
*
* It checks privileges to relabel the supplied function
diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c
index efce914..259be49 100644
--- a/contrib/sepgsql/relation.c
+++ b/contrib/sepgsql/relation.c
@@ -21,6 +21,7 @@
#include "commands/seclabel.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/tqual.h"
#include "sepgsql.h"
@@ -110,6 +111,36 @@ sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
}
/*
+ * sepgsql_attribute_drop
+ *
+ * It checks privileges to drop the supplied column.
+ */
+void
+sepgsql_attribute_drop(Oid relOid, AttrNumber attnum)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ if (get_rel_relkind(relOid) != RELKIND_RELATION)
+ return;
+
+ /*
+ * check db_column:{drop} permission
+ */
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attnum;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_DB_COLUMN__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_attribute_relabel
*
* It checks privileges to relabel the supplied column
@@ -310,6 +341,94 @@ out:
}
/*
+ * sepgsql_relation_drop
+ *
+ * It checks privileges to drop the supplied relation.
+ */
+void
+sepgsql_relation_drop(Oid relOid)
+{
+ ObjectAddress object;
+ char *audit_name;
+ uint16_t tclass = 0;
+ 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;
+
+ /*
+ * check db_schema:{remove_name} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = get_rel_namespace(relOid);
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__REMOVE_NAME,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_table/sequence/view:{drop} permission
+ */
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ tclass,
+ SEPG_DB_TABLE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_column:{drop} permission
+ */
+ if (relkind == RELKIND_RELATION)
+ {
+ Form_pg_attribute attForm;
+ CatCList *attrList;
+ HeapTuple atttup;
+ int i;
+
+ attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid));
+ for (i=0; i < attrList->n_members; i++)
+ {
+ atttup = &attrList->members[i]->tuple;
+ attForm = (Form_pg_attribute) GETSTRUCT(atttup);
+
+ if (attForm->attisdropped)
+ continue;
+
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attForm->attnum;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_DB_COLUMN__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+ }
+ ReleaseCatCacheList(attrList);
+ }
+}
+
+/*
* sepgsql_relation_relabel
*
* It checks privileges to relabel the supplied relation by the `seclabel'.
diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c
index 90dca1d..31d60ef 100644
--- a/contrib/sepgsql/schema.c
+++ b/contrib/sepgsql/schema.c
@@ -97,6 +97,33 @@ sepgsql_schema_post_create(Oid namespaceId)
}
/*
+ * sepgsql_schema_drop
+ *
+ * It checks privileges to drop the supplied schema object.
+ */
+void
+sepgsql_schema_drop(Oid namespaceId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_schema:{drop} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = namespaceId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_schema_relabel
*
* It checks privileges to relabel the supplied schema
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index c93da7a..a7a4532 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -288,27 +288,32 @@ extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort);
*/
extern void sepgsql_database_post_create(Oid databaseId,
const char *dtemplate);
+extern void sepgsql_database_drop(Oid databaseId);
extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel);
/*
* schema.c
*/
extern void sepgsql_schema_post_create(Oid namespaceId);
+extern void sepgsql_schema_drop(Oid namespaceId);
extern void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel);
/*
* relation.c
*/
extern void sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum);
+extern void sepgsql_attribute_drop(Oid relOid, AttrNumber attnum);
extern void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
const char *seclabel);
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);
/*
* proc.c
*/
extern void sepgsql_proc_post_create(Oid functionId);
+extern void sepgsql_proc_drop(Oid functionId);
extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel);
#endif /* SEPGSQL_H */
diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql
deleted file mode 100644
index b0695b4..0000000
--- a/contrib/sepgsql/sql/create.sql
+++ /dev/null
@@ -1,46 +0,0 @@
---
--- Regression Test for Creation of Object Permission Checks
---
-
--- confirm required permissions using audit messages
--- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
-SET sepgsql.debug_audit = true;
-SET client_min_messages = LOG;
-
-CREATE DATABASE regtest_sepgsql_test_database;
-
-CREATE SCHEMA regtest_schema;
-
-SET search_path = regtest_schema, public;
-
-CREATE TABLE regtest_table (x serial primary key, y text);
-
-ALTER TABLE regtest_table ADD COLUMN z int;
-
-CREATE TABLE regtest_table_2 (a int) WITH OIDS;
-
--- corresponding toast table should not have label and permission checks
-ALTER TABLE regtest_table_2 ADD COLUMN b text;
-
--- VACUUM FULL internally create a new table and swap them later.
-VACUUM FULL regtest_table;
-
-CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
-
-CREATE SEQUENCE regtest_seq;
-
-CREATE TYPE regtest_comptype AS (a int, b text);
-
-CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
- AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
-
-CREATE AGGREGATE regtest_agg (
- sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
-);
-
---
--- clean-up
---
-DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
-
-DROP SCHEMA IF EXISTS regtest_schema CASCADE;
diff --git a/contrib/sepgsql/sql/ddl.sql b/contrib/sepgsql/sql/ddl.sql
new file mode 100644
index 0000000..8dd57e0
--- /dev/null
+++ b/contrib/sepgsql/sql/ddl.sql
@@ -0,0 +1,81 @@
+--
+-- Regression Test for DDL of Object Permission Checks
+--
+
+-- confirm required permissions using audit messages
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+
+--
+-- CREATE Permission checks
+--
+CREATE DATABASE regtest_sepgsql_test_database;
+
+CREATE USER regtest_sepgsql_test_user;
+
+CREATE SCHEMA regtest_schema;
+
+GRANT ALL ON SCHEMA regtest_schema TO regtest_sepgsql_test_user;
+
+SET search_path = regtest_schema, public;
+
+CREATE TABLE regtest_table (x serial primary key, y text);
+
+ALTER TABLE regtest_table ADD COLUMN z int;
+
+CREATE TABLE regtest_table_2 (a int) WITH OIDS;
+
+-- corresponding toast table should not have label and permission checks
+ALTER TABLE regtest_table_2 ADD COLUMN b text;
+
+-- VACUUM FULL internally create a new table and swap them later.
+VACUUM FULL regtest_table;
+
+CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
+
+CREATE SEQUENCE regtest_seq;
+
+CREATE TYPE regtest_comptype AS (a int, b text);
+
+CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
+
+CREATE AGGREGATE regtest_agg (
+ sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
+);
+
+-- CREATE objects owned by others
+SET SESSION AUTHORIZATION regtest_sepgsql_test_user;
+
+SET search_path = regtest_schema, public;
+
+CREATE TABLE regtest_table_3 (x int, y serial);
+
+CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < y;
+
+CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RETURN $1 * $1 < 100; END';
+
+RESET SESSION AUTHORIZATION;
+
+--
+-- DROP Permission checks (with clean-up)
+--
+
+DROP FUNCTION regtest_func(text,int[]);
+DROP AGGREGATE regtest_agg(int);
+
+DROP SEQUENCE regtest_seq;
+DROP VIEW regtest_view;
+
+ALTER TABLE regtest_table DROP COLUMN y;
+ALTER TABLE regtest_table_2 SET WITHOUT OIDS;
+
+DROP TABLE regtest_table;
+
+DROP OWNED BY regtest_sepgsql_test_user;
+
+DROP DATABASE regtest_sepgsql_test_database;
+DROP USER regtest_sepgsql_test_user;
+DROP SCHEMA IF EXISTS regtest_schema CASCADE;
diff --git a/contrib/sepgsql/test_sepgsql b/contrib/sepgsql/test_sepgsql
index 52237e6..473004f 100755
--- a/contrib/sepgsql/test_sepgsql
+++ b/contrib/sepgsql/test_sepgsql
@@ -259,6 +259,6 @@ echo "found ${NUM}"
echo
echo "============== running sepgsql regression tests =============="
-make REGRESS="label dml create misc" REGRESS_OPTS="--launcher ./launcher" installcheck
+make REGRESS="label dml ddl misc" REGRESS_OPTS="--launcher ./launcher" installcheck
# exit with the exit code provided by "make"
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index e45c258..d9b1fed 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -448,6 +448,20 @@ UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
</para>
<para>
+ When <literal>DROP</> command is executed, <literal>drop</> will be
+ checked on the object being removed for each object types.
+ Please note that it shall not be checked on the objects removed by
+ cascaded deletion according to the standard manner in SQL.
+ </para>
+ <para>
+ A few additional checks are applied depending on object types.
+ On deletion of objects underlying a particula schema (tables, views,
+ sequences and procedures), <literal>remove_name</> shall be also checked
+ on the schema, not only <literal>drop</> on the object being removed
+ itself.
+ </para>
+
+ <para>
When <xref linkend="sql-security-label"> is executed, <literal>setattr</>
and <literal>relabelfrom</> will be checked on the object being relabeled
with its old security label, then <literal>relabelto</> with the supplied
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index db86262..81da918 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -20,6 +20,7 @@
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
@@ -972,6 +973,11 @@ deleteOneObject(const ObjectAddress *object, Relation depRel, int flags)
SysScanDesc scan;
HeapTuple tup;
+ /* DROP hook of the objects being removed */
+ InvokeObjectAccessHookArg(OAT_DROP, object->classId,
+ object->objectId, object->objectSubId,
+ Int32GetDatum(flags));
+
/*
* 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/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 42a8b31..ca12369 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -777,6 +777,9 @@ dropdb(const char *dbname, bool missing_ok)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
dbname);
+ /* DROP hook for the database being removed */
+ InvokeObjectAccessHook(OAT_DROP, DatabaseRelationId, db_id, 0);
+
/*
* Disallow dropping a DB that is marked istemplate. This is just to
* prevent people from accidentally dropping template0 or template1; they
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index ff58490..bbfdc41 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -428,6 +428,9 @@ DropTableSpace(DropTableSpaceStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE,
tablespacename);
+ /* DROP hook for the tablespace being removed */
+ InvokeObjectAccessHook(OAT_DROP, TableSpaceRelationId, tablespaceoid, 0);
+
/* Disallow drop of the standard tablespaces, even by superuser */
if (tablespaceoid == GLOBALTABLESPACE_OID ||
tablespaceoid == DEFAULTTABLESPACE_OID)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 9a88c90..961f9cc 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -932,6 +932,9 @@ DropRole(DropRoleStmt *stmt)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to drop superusers")));
+ /* DROP hook for the role being removed */
+ InvokeObjectAccessHook(OAT_DROP, AuthIdRelationId, roleid, 0);
+
/*
* Lock the role, so nobody can add dependencies to her while we drop
* her. We keep the lock until the end of transaction.
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 5c7a40a..25e2a3c 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -19,11 +19,16 @@
* Typically, this is done after inserting the primary catalog records and
* associated dependencies.
*
+ * OAT_DROP should be invoked just before deletion of objects; typically
+ * deleteOneObject(). It takes an argument to inform the context of object
+ * being removed.
+ *
* Other types may be added in the future.
*/
typedef enum ObjectAccessType
{
OAT_POST_CREATE,
+ OAT_DROP,
} ObjectAccessType;
/*
@@ -33,14 +38,22 @@ typedef enum ObjectAccessType
typedef void (*object_access_hook_type) (ObjectAccessType access,
Oid classId,
Oid objectId,
- int subId);
+ int subId,
+ Datum arg);
extern PGDLLIMPORT object_access_hook_type object_access_hook;
#define InvokeObjectAccessHook(access,classId,objectId,subId) \
do { \
if (object_access_hook) \
- (*object_access_hook)((access),(classId),(objectId),(subId)); \
+ (*object_access_hook)((access),(classId),(objectId),(subId),0); \
+ } while(0)
+
+#define InvokeObjectAccessHookArg(access,classId,objectId,subId,arg) \
+ do { \
+ if (object_access_hook) \
+ (*object_access_hook)((access),(classId), \
+ (objectId),(subId),(arg)); \
} while(0)
#endif /* OBJECTACCESS_H */
On Thu, Jan 26, 2012 at 7:27 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
It seems to me reasonable design.
The attached patch is rebased one according to your perform-deletion patch.
That looks pretty sensible. But I don't think this is true any more:
+ Please note that it shall not be checked on the objects removed by
+ cascaded deletion according to the standard manner in SQL.
I've been thinking more about the question of object access hooks with
arguments as well. In a couple of designs you've proposed, we've
needed InvokeObjectAccessHook0..11 or whatever, and I don't like that
design - it seems grotty and not type-safe. However, looking at what
you did in this patch, I have another idea. Suppose that we add ONE
additional argument to the object access hook function, as you've done
here, and if a particular type of hook invocation needs multiple
arguments, it can pass them with a struct. In fact, let's use a
struct regardless, for uniformity, and pass the value as a void *.
That is:
typedef struct ObjectAccessDrop {
int dropflags;
} ObjectAccessDrop;
At the call site, we do this:
if (object_access_hook)
{
ObjectAccessDrop arg;
arg.dropflags = flags;
InvokeObjectAccessHook(..., arg);
}
If there's no argument, then we can just do:
InvokeObjectAccessHook(..., NULL);
The advantage of this is that if we change the structure definition,
loadable modules compiled against a newer code base should either (1)
still work or (2) fail to compile. The way we have it right now, if
we decide to pass a second argument (say, the DropBehavior) to the
hook, we're potentially going to silently break sepgsql no matter how
we do it. But if we enforce use of a struct, then the only thing the
client should ever be doing with the argument it gets is casting it to
ObjectAccessDrop *. Then, if we've added fields to the struct, the
code will still do the right thing even if the field order has been
changed or whatever. If we've removed fields or changed their data
types, things should blow up fairly obviously instead of seeming to
work but actually failing.
Thoughts?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2012/1/26 Robert Haas <robertmhaas@gmail.com>:
On Thu, Jan 26, 2012 at 7:27 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
It seems to me reasonable design.
The attached patch is rebased one according to your perform-deletion patch.That looks pretty sensible. But I don't think this is true any more:
+ Please note that it shall not be checked on the objects removed by + cascaded deletion according to the standard manner in SQL.I've been thinking more about the question of object access hooks with
arguments as well. In a couple of designs you've proposed, we've
needed InvokeObjectAccessHook0..11 or whatever, and I don't like that
design - it seems grotty and not type-safe. However, looking at what
you did in this patch, I have another idea. Suppose that we add ONE
additional argument to the object access hook function, as you've done
here, and if a particular type of hook invocation needs multiple
arguments, it can pass them with a struct. In fact, let's use a
struct regardless, for uniformity, and pass the value as a void *.
That is:typedef struct ObjectAccessDrop {
int dropflags;
} ObjectAccessDrop;At the call site, we do this:
if (object_access_hook)
{
ObjectAccessDrop arg;
arg.dropflags = flags;
InvokeObjectAccessHook(..., arg);
}If there's no argument, then we can just do:
InvokeObjectAccessHook(..., NULL);
The advantage of this is that if we change the structure definition,
loadable modules compiled against a newer code base should either (1)
still work or (2) fail to compile. The way we have it right now, if
we decide to pass a second argument (say, the DropBehavior) to the
hook, we're potentially going to silently break sepgsql no matter how
we do it. But if we enforce use of a struct, then the only thing the
client should ever be doing with the argument it gets is casting it to
ObjectAccessDrop *. Then, if we've added fields to the struct, the
code will still do the right thing even if the field order has been
changed or whatever. If we've removed fields or changed their data
types, things should blow up fairly obviously instead of seeming to
work but actually failing.Thoughts?
I also think your idea is good; flexible and reliable toward future enhancement.
If we have one point to improve this idea, is it needed to deliver
"access", "classid",
"objectid" and "subid" as separated argument?
If we define a type to deliver information on object access hook as follows:
typedef struct {
ObjectAccessType access;
ObjectAddress address;
union {
struct {
int flags;
} drop;
} arg;
} ObjectAccessHookData;
All the argument that object_access_hook takes should be a pointer of this
structure only, and no need to type cast on the module side.
One disadvantage is that it needs to set up this structure on caller
side including
ObjectAccessType and ObjectAddress information. However, it can be embedded
within preprocessor macro to keep nums of lines as currently we do.
example:
#define InvaokeDropAccessHook(classid, objectid, objsubid, flags) \
do {
if (object_access_hook)
{
ObjectAccessHookData __hook_data;
__hook_data.access = OAT_DROP;
__hook_data.address.classId = (classid);
__hook_data.address.objectId = (objectid);
__hook_data.address.objectSubid = (objsubid);
__hook_data.args.drop.flags = (flags);
(*object_access_hook)(&__hook_data);
}
} while (0)
How about your opinion?
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
On Sat, Jan 28, 2012 at 1:53 PM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
2012/1/26 Robert Haas <robertmhaas@gmail.com>:
On Thu, Jan 26, 2012 at 7:27 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
It seems to me reasonable design.
The attached patch is rebased one according to your perform-deletion patch.That looks pretty sensible. But I don't think this is true any more:
+ Please note that it shall not be checked on the objects removed by + cascaded deletion according to the standard manner in SQL.I've been thinking more about the question of object access hooks with
arguments as well. In a couple of designs you've proposed, we've
needed InvokeObjectAccessHook0..11 or whatever, and I don't like that
design - it seems grotty and not type-safe. However, looking at what
you did in this patch, I have another idea. Suppose that we add ONE
additional argument to the object access hook function, as you've done
here, and if a particular type of hook invocation needs multiple
arguments, it can pass them with a struct. In fact, let's use a
struct regardless, for uniformity, and pass the value as a void *.
That is:typedef struct ObjectAccessDrop {
int dropflags;
} ObjectAccessDrop;At the call site, we do this:
if (object_access_hook)
{
ObjectAccessDrop arg;
arg.dropflags = flags;
InvokeObjectAccessHook(..., arg);
}If there's no argument, then we can just do:
InvokeObjectAccessHook(..., NULL);
The advantage of this is that if we change the structure definition,
loadable modules compiled against a newer code base should either (1)
still work or (2) fail to compile. The way we have it right now, if
we decide to pass a second argument (say, the DropBehavior) to the
hook, we're potentially going to silently break sepgsql no matter how
we do it. But if we enforce use of a struct, then the only thing the
client should ever be doing with the argument it gets is casting it to
ObjectAccessDrop *. Then, if we've added fields to the struct, the
code will still do the right thing even if the field order has been
changed or whatever. If we've removed fields or changed their data
types, things should blow up fairly obviously instead of seeming to
work but actually failing.Thoughts?
I also think your idea is good; flexible and reliable toward future enhancement.
If we have one point to improve this idea, is it needed to deliver
"access", "classid",
"objectid" and "subid" as separated argument?If we define a type to deliver information on object access hook as follows:
typedef struct {
ObjectAccessType access;
ObjectAddress address;
union {
struct {
int flags;
} drop;
} arg;
} ObjectAccessHookData;All the argument that object_access_hook takes should be a pointer of this
structure only, and no need to type cast on the module side.One disadvantage is that it needs to set up this structure on caller
side including
ObjectAccessType and ObjectAddress information. However, it can be embedded
within preprocessor macro to keep nums of lines as currently we do.example:
#define InvaokeDropAccessHook(classid, objectid, objsubid, flags) \
do {
if (object_access_hook)
{
ObjectAccessHookData __hook_data;__hook_data.access = OAT_DROP;
__hook_data.address.classId = (classid);
__hook_data.address.objectId = (objectid);
__hook_data.address.objectSubid = (objsubid);
__hook_data.args.drop.flags = (flags);(*object_access_hook)(&__hook_data);
}
} while (0)How about your opinion?
I don't see any real advantage of that. One advantage of the current
design is that any hook types which *don't* require extra arguments
need not set up and pass a structure; they can just pass NULL. So I
suggest we keep classid, objid, and subid as separate arguments, and
just add one new one which can be type-specific.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2012/2/3 Robert Haas <robertmhaas@gmail.com>:
On Sat, Jan 28, 2012 at 1:53 PM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
2012/1/26 Robert Haas <robertmhaas@gmail.com>:
On Thu, Jan 26, 2012 at 7:27 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
It seems to me reasonable design.
The attached patch is rebased one according to your perform-deletion patch.That looks pretty sensible. But I don't think this is true any more:
+ Please note that it shall not be checked on the objects removed by + cascaded deletion according to the standard manner in SQL.I've been thinking more about the question of object access hooks with
arguments as well. In a couple of designs you've proposed, we've
needed InvokeObjectAccessHook0..11 or whatever, and I don't like that
design - it seems grotty and not type-safe. However, looking at what
you did in this patch, I have another idea. Suppose that we add ONE
additional argument to the object access hook function, as you've done
here, and if a particular type of hook invocation needs multiple
arguments, it can pass them with a struct. In fact, let's use a
struct regardless, for uniformity, and pass the value as a void *.
That is:typedef struct ObjectAccessDrop {
int dropflags;
} ObjectAccessDrop;At the call site, we do this:
if (object_access_hook)
{
ObjectAccessDrop arg;
arg.dropflags = flags;
InvokeObjectAccessHook(..., arg);
}If there's no argument, then we can just do:
InvokeObjectAccessHook(..., NULL);
The advantage of this is that if we change the structure definition,
loadable modules compiled against a newer code base should either (1)
still work or (2) fail to compile. The way we have it right now, if
we decide to pass a second argument (say, the DropBehavior) to the
hook, we're potentially going to silently break sepgsql no matter how
we do it. But if we enforce use of a struct, then the only thing the
client should ever be doing with the argument it gets is casting it to
ObjectAccessDrop *. Then, if we've added fields to the struct, the
code will still do the right thing even if the field order has been
changed or whatever. If we've removed fields or changed their data
types, things should blow up fairly obviously instead of seeming to
work but actually failing.Thoughts?
I also think your idea is good; flexible and reliable toward future enhancement.
If we have one point to improve this idea, is it needed to deliver
"access", "classid",
"objectid" and "subid" as separated argument?If we define a type to deliver information on object access hook as follows:
typedef struct {
ObjectAccessType access;
ObjectAddress address;
union {
struct {
int flags;
} drop;
} arg;
} ObjectAccessHookData;All the argument that object_access_hook takes should be a pointer of this
structure only, and no need to type cast on the module side.One disadvantage is that it needs to set up this structure on caller
side including
ObjectAccessType and ObjectAddress information. However, it can be embedded
within preprocessor macro to keep nums of lines as currently we do.example:
#define InvaokeDropAccessHook(classid, objectid, objsubid, flags) \
do {
if (object_access_hook)
{
ObjectAccessHookData __hook_data;__hook_data.access = OAT_DROP;
__hook_data.address.classId = (classid);
__hook_data.address.objectId = (objectid);
__hook_data.address.objectSubid = (objsubid);
__hook_data.args.drop.flags = (flags);(*object_access_hook)(&__hook_data);
}
} while (0)How about your opinion?
I don't see any real advantage of that. One advantage of the current
design is that any hook types which *don't* require extra arguments
need not set up and pass a structure; they can just pass NULL. So I
suggest we keep classid, objid, and subid as separate arguments, and
just add one new one which can be type-specific.
# I send it again with "reply-all".
OK, I modified the patch according to your suggestions.
object_access_hook was extended to take an argument of void * pointer,
and InvokeObjectAccessHook was also allows to deliver it.
On OAT_DROP event, its invocation is enclosed by if-block as:
+ /* DROP hook of the objects being removed */
+ if (object_access_hook)
+ {
+ ObjectAccessDrop drop_arg;
+ drop_arg.dropflags = flags;
+ InvokeObjectAccessHook(OAT_DROP, object->classId, object->objectId,
+ object->objectSubId, &drop_arg);
+ }
Should we have InvokeObjectDropHook() macro to provide
a series of invocation process with OAT_DROP event, instead
of the flat if-block?
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
Attachments:
pgsql-v9.2-drop-permissions.v5.patchapplication/octet-stream; name=pgsql-v9.2-drop-permissions.v5.patchDownload
contrib/sepgsql/database.c | 27 +++++
contrib/sepgsql/expected/create.out | 80 --------------
contrib/sepgsql/expected/ddl.out | 164 ++++++++++++++++++++++++++++
contrib/sepgsql/hooks.c | 46 ++++++++-
contrib/sepgsql/proc.c | 42 +++++++
contrib/sepgsql/relation.c | 119 ++++++++++++++++++++
contrib/sepgsql/schema.c | 27 +++++
contrib/sepgsql/sepgsql.h | 5 +
contrib/sepgsql/sql/create.sql | 46 --------
contrib/sepgsql/sql/ddl.sql | 81 ++++++++++++++
contrib/sepgsql/test_sepgsql | 2 +-
doc/src/sgml/sepgsql.sgml | 14 +++
src/backend/catalog/dependency.c | 10 ++
src/backend/catalog/heap.c | 3 +-
src/backend/catalog/pg_collation.c | 2 +-
src/backend/catalog/pg_constraint.c | 3 +-
src/backend/catalog/pg_conversion.c | 4 +-
src/backend/catalog/pg_namespace.c | 3 +-
src/backend/catalog/pg_operator.c | 4 +-
src/backend/catalog/pg_proc.c | 3 +-
src/backend/catalog/pg_type.c | 6 +-
src/backend/commands/dbcommands.c | 12 ++-
src/backend/commands/extension.c | 2 +-
src/backend/commands/foreigncmds.c | 8 +-
src/backend/commands/functioncmds.c | 3 +-
src/backend/commands/opclasscmds.c | 4 +-
src/backend/commands/proclang.c | 2 +-
src/backend/commands/tablecmds.c | 2 +-
src/backend/commands/tablespace.c | 11 ++-
src/backend/commands/trigger.c | 2 +-
src/backend/commands/tsearchcmds.c | 11 +-
src/backend/commands/user.c | 12 ++-
src/backend/rewrite/rewriteDefine.c | 2 +-
src/backend/storage/large_object/inv_api.c | 2 +-
src/include/catalog/objectaccess.h | 29 ++++-
35 files changed, 628 insertions(+), 165 deletions(-)
diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c
index be3a7be..0c395c4 100644
--- a/contrib/sepgsql/database.c
+++ b/contrib/sepgsql/database.c
@@ -119,6 +119,33 @@ sepgsql_database_post_create(Oid databaseId, const char *dtemplate)
}
/*
+ * sepgsql_database_drop
+ *
+ * It checks privileges to drop the supplied database
+ */
+void
+sepgsql_database_drop(Oid databaseId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_database:{drop} permission
+ */
+ object.classId = DatabaseRelationId;
+ object.objectId = databaseId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_DATABASE,
+ SEPG_DB_DATABASE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_database_relabel
*
* It checks privileges to relabel the supplied database with the `seclabel'
diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out
deleted file mode 100644
index 0f04a3e..0000000
--- a/contrib/sepgsql/expected/create.out
+++ /dev/null
@@ -1,80 +0,0 @@
---
--- Regression Test for Creation of Object Permission Checks
---
--- confirm required permissions using audit messages
-SELECT sepgsql_getcon(); -- confirm client privilege
- sepgsql_getcon
--------------------------------------------
- unconfined_u:unconfined_r:unconfined_t:s0
-(1 row)
-
-SET sepgsql.debug_audit = true;
-SET client_min_messages = LOG;
-CREATE DATABASE regtest_sepgsql_test_database;
-LOG: SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
-LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
-CREATE SCHEMA regtest_schema;
-LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
-SET search_path = regtest_schema, public;
-CREATE TABLE regtest_table (x serial primary key, y text);
-NOTICE: CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x"
-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_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq"
-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"
-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 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 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 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 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 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 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"
-NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for 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;
-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_2"
-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_2 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_2 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_2 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_2 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_2 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_2 column oid"
-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_2 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_2 column a"
--- corresponding toast table should not have label and permission checks
-ALTER TABLE regtest_table_2 ADD COLUMN b text;
-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_2 column b"
--- VACUUM FULL internally create a new table and swap them later.
-VACUUM FULL regtest_table;
-CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
-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_view_t:s0 tclass=db_view name="view regtest_view"
-CREATE SEQUENCE regtest_seq;
-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_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
-CREATE TYPE regtest_comptype AS (a int, b text);
-CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
- AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
-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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
-CREATE AGGREGATE regtest_agg (
- sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
-);
-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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
---
--- clean-up
---
-DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
-DROP SCHEMA IF EXISTS regtest_schema CASCADE;
-NOTICE: drop cascades to 7 other objects
-DETAIL: drop cascades to table regtest_table
-drop cascades to table regtest_table_2
-drop cascades to view regtest_view
-drop cascades to sequence regtest_seq
-drop cascades to type regtest_comptype
-drop cascades to function regtest_func(text,integer[])
-drop cascades to function regtest_agg(integer)
diff --git a/contrib/sepgsql/expected/ddl.out b/contrib/sepgsql/expected/ddl.out
new file mode 100644
index 0000000..1c7bcc5
--- /dev/null
+++ b/contrib/sepgsql/expected/ddl.out
@@ -0,0 +1,164 @@
+--
+-- Regression Test for DDL of Object Permission Checks
+--
+-- confirm required permissions using audit messages
+SELECT sepgsql_getcon(); -- confirm client privilege
+ sepgsql_getcon
+-------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0
+(1 row)
+
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+--
+-- CREATE Permission checks
+--
+CREATE DATABASE regtest_sepgsql_test_database;
+LOG: SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
+LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
+CREATE USER regtest_sepgsql_test_user;
+CREATE SCHEMA regtest_schema;
+LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+GRANT ALL ON SCHEMA regtest_schema TO regtest_sepgsql_test_user;
+SET search_path = regtest_schema, public;
+CREATE TABLE regtest_table (x serial primary key, y text);
+NOTICE: CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x"
+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_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq"
+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"
+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 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 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 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 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 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 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"
+NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for 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;
+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_2"
+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_2 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_2 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_2 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_2 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_2 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_2 column oid"
+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_2 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_2 column a"
+-- corresponding toast table should not have label and permission checks
+ALTER TABLE regtest_table_2 ADD COLUMN b text;
+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_2 column b"
+-- VACUUM FULL internally create a new table and swap them later.
+VACUUM FULL regtest_table;
+CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
+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_view_t:s0 tclass=db_view name="view regtest_view"
+CREATE SEQUENCE regtest_seq;
+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_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
+CREATE TYPE regtest_comptype AS (a int, b text);
+CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
+CREATE AGGREGATE regtest_agg (
+ sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
+);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
+-- CREATE objects owned by others
+SET SESSION AUTHORIZATION regtest_sepgsql_test_user;
+SET search_path = regtest_schema, public;
+CREATE TABLE regtest_table_3 (x int, y serial);
+NOTICE: CREATE TABLE will create implicit sequence "regtest_table_3_y_seq" for serial column "regtest_table_3.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 { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_3_y_seq"
+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_3"
+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_3 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_3 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_3 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_3 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_3 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_3 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_3 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_3 column y"
+CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < 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 { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view_2"
+CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RETURN $1 * $1 < 100; END';
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_2(integer)"
+RESET SESSION AUTHORIZATION;
+--
+-- DROP Permission checks (with clean-up)
+--
+DROP FUNCTION regtest_func(text,int[]);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text,integer[])"
+DROP AGGREGATE regtest_agg(int);
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
+DROP SEQUENCE regtest_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 { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
+DROP VIEW regtest_view;
+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_view_t:s0 tclass=db_view name="view regtest_view"
+ALTER TABLE regtest_table DROP 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 column y"
+ALTER TABLE regtest_table_2 SET WITHOUT OIDS;
+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_2 column oid"
+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 { 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"
+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 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 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 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 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 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 column z"
+DROP OWNED BY regtest_sepgsql_test_user;
+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_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_2(integer)"
+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_view_t:s0 tclass=db_view name="view regtest_view_2"
+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_3_y_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 { 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_3"
+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_3 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_3 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_3 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_3 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_3 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_3 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_3 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_3 column y"
+DROP DATABASE regtest_sepgsql_test_database;
+LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database"
+DROP USER regtest_sepgsql_test_user;
+DROP SCHEMA IF EXISTS regtest_schema CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table regtest_table_2
+drop cascades to type regtest_comptype
+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_2"
+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_2 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_2 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_2 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_2 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_2 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_2 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_2 column a"
+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_2 column b"
+LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 47437ba..9823d46 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -10,6 +10,7 @@
*/
#include "postgres.h"
+#include "catalog/dependency.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_class.h"
#include "catalog/pg_database.h"
@@ -133,10 +134,11 @@ static void
sepgsql_object_access(ObjectAccessType access,
Oid classId,
Oid objectId,
- int subId)
+ int subId,
+ void *arg)
{
if (next_object_access_hook)
- (*next_object_access_hook) (access, classId, objectId, subId);
+ (*next_object_access_hook) (access, classId, objectId, subId, arg);
switch (access)
{
@@ -192,6 +194,46 @@ sepgsql_object_access(ObjectAccessType access,
}
break;
+ case OAT_DROP:
+ {
+ ObjectAccessDrop *drop_arg = (ObjectAccessDrop *)arg;
+
+ /*
+ * No need to apply permission checks on object deletion
+ * due to internal cleanups; such as removal of temporary
+ * database object on session closed.
+ */
+ if ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) != 0)
+ break;
+
+ switch (classId)
+ {
+ case DatabaseRelationId:
+ sepgsql_database_drop(objectId);
+ break;
+
+ case NamespaceRelationId:
+ sepgsql_schema_drop(objectId);
+ break;
+
+ case RelationRelationId:
+ if (subId == 0)
+ sepgsql_relation_drop(objectId);
+ else
+ sepgsql_attribute_drop(objectId, subId);
+ break;
+
+ case ProcedureRelationId:
+ sepgsql_proc_drop(objectId);
+ break;
+
+ default:
+ /* Ignore unsupported object classes */
+ break;
+ }
+ }
+ break;
+
default:
elog(ERROR, "unexpected object access type: %d", (int) access);
break;
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
index b902797..1efbc90 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -131,6 +131,48 @@ sepgsql_proc_post_create(Oid functionId)
}
/*
+ * sepgsql_proc_drop
+ *
+ * It checks privileges to drop the supplied function.
+ */
+void
+sepgsql_proc_drop(Oid functionId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_schema:{remove_name} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = get_func_namespace(functionId);
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__REMOVE_NAME,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_procedure:{drop} permission
+ */
+ object.classId = ProcedureRelationId;
+ object.objectId = functionId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_DB_PROCEDURE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_proc_relabel
*
* It checks privileges to relabel the supplied function
diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c
index efce914..259be49 100644
--- a/contrib/sepgsql/relation.c
+++ b/contrib/sepgsql/relation.c
@@ -21,6 +21,7 @@
#include "commands/seclabel.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/tqual.h"
#include "sepgsql.h"
@@ -110,6 +111,36 @@ sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
}
/*
+ * sepgsql_attribute_drop
+ *
+ * It checks privileges to drop the supplied column.
+ */
+void
+sepgsql_attribute_drop(Oid relOid, AttrNumber attnum)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ if (get_rel_relkind(relOid) != RELKIND_RELATION)
+ return;
+
+ /*
+ * check db_column:{drop} permission
+ */
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attnum;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_DB_COLUMN__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_attribute_relabel
*
* It checks privileges to relabel the supplied column
@@ -310,6 +341,94 @@ out:
}
/*
+ * sepgsql_relation_drop
+ *
+ * It checks privileges to drop the supplied relation.
+ */
+void
+sepgsql_relation_drop(Oid relOid)
+{
+ ObjectAddress object;
+ char *audit_name;
+ uint16_t tclass = 0;
+ 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;
+
+ /*
+ * check db_schema:{remove_name} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = get_rel_namespace(relOid);
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__REMOVE_NAME,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_table/sequence/view:{drop} permission
+ */
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ tclass,
+ SEPG_DB_TABLE__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+
+ /*
+ * check db_column:{drop} permission
+ */
+ if (relkind == RELKIND_RELATION)
+ {
+ Form_pg_attribute attForm;
+ CatCList *attrList;
+ HeapTuple atttup;
+ int i;
+
+ attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid));
+ for (i=0; i < attrList->n_members; i++)
+ {
+ atttup = &attrList->members[i]->tuple;
+ attForm = (Form_pg_attribute) GETSTRUCT(atttup);
+
+ if (attForm->attisdropped)
+ continue;
+
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attForm->attnum;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_DB_COLUMN__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+ }
+ ReleaseCatCacheList(attrList);
+ }
+}
+
+/*
* sepgsql_relation_relabel
*
* It checks privileges to relabel the supplied relation by the `seclabel'.
diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c
index 90dca1d..31d60ef 100644
--- a/contrib/sepgsql/schema.c
+++ b/contrib/sepgsql/schema.c
@@ -97,6 +97,33 @@ sepgsql_schema_post_create(Oid namespaceId)
}
/*
+ * sepgsql_schema_drop
+ *
+ * It checks privileges to drop the supplied schema object.
+ */
+void
+sepgsql_schema_drop(Oid namespaceId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_schema:{drop} permission
+ */
+ object.classId = NamespaceRelationId;
+ object.objectId = namespaceId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__DROP,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
+
+/*
* sepgsql_schema_relabel
*
* It checks privileges to relabel the supplied schema
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index c93da7a..a7a4532 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -288,27 +288,32 @@ extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort);
*/
extern void sepgsql_database_post_create(Oid databaseId,
const char *dtemplate);
+extern void sepgsql_database_drop(Oid databaseId);
extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel);
/*
* schema.c
*/
extern void sepgsql_schema_post_create(Oid namespaceId);
+extern void sepgsql_schema_drop(Oid namespaceId);
extern void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel);
/*
* relation.c
*/
extern void sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum);
+extern void sepgsql_attribute_drop(Oid relOid, AttrNumber attnum);
extern void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
const char *seclabel);
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);
/*
* proc.c
*/
extern void sepgsql_proc_post_create(Oid functionId);
+extern void sepgsql_proc_drop(Oid functionId);
extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel);
#endif /* SEPGSQL_H */
diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql
deleted file mode 100644
index b0695b4..0000000
--- a/contrib/sepgsql/sql/create.sql
+++ /dev/null
@@ -1,46 +0,0 @@
---
--- Regression Test for Creation of Object Permission Checks
---
-
--- confirm required permissions using audit messages
--- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
-SET sepgsql.debug_audit = true;
-SET client_min_messages = LOG;
-
-CREATE DATABASE regtest_sepgsql_test_database;
-
-CREATE SCHEMA regtest_schema;
-
-SET search_path = regtest_schema, public;
-
-CREATE TABLE regtest_table (x serial primary key, y text);
-
-ALTER TABLE regtest_table ADD COLUMN z int;
-
-CREATE TABLE regtest_table_2 (a int) WITH OIDS;
-
--- corresponding toast table should not have label and permission checks
-ALTER TABLE regtest_table_2 ADD COLUMN b text;
-
--- VACUUM FULL internally create a new table and swap them later.
-VACUUM FULL regtest_table;
-
-CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
-
-CREATE SEQUENCE regtest_seq;
-
-CREATE TYPE regtest_comptype AS (a int, b text);
-
-CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
- AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
-
-CREATE AGGREGATE regtest_agg (
- sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
-);
-
---
--- clean-up
---
-DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
-
-DROP SCHEMA IF EXISTS regtest_schema CASCADE;
diff --git a/contrib/sepgsql/sql/ddl.sql b/contrib/sepgsql/sql/ddl.sql
new file mode 100644
index 0000000..8dd57e0
--- /dev/null
+++ b/contrib/sepgsql/sql/ddl.sql
@@ -0,0 +1,81 @@
+--
+-- Regression Test for DDL of Object Permission Checks
+--
+
+-- confirm required permissions using audit messages
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+
+--
+-- CREATE Permission checks
+--
+CREATE DATABASE regtest_sepgsql_test_database;
+
+CREATE USER regtest_sepgsql_test_user;
+
+CREATE SCHEMA regtest_schema;
+
+GRANT ALL ON SCHEMA regtest_schema TO regtest_sepgsql_test_user;
+
+SET search_path = regtest_schema, public;
+
+CREATE TABLE regtest_table (x serial primary key, y text);
+
+ALTER TABLE regtest_table ADD COLUMN z int;
+
+CREATE TABLE regtest_table_2 (a int) WITH OIDS;
+
+-- corresponding toast table should not have label and permission checks
+ALTER TABLE regtest_table_2 ADD COLUMN b text;
+
+-- VACUUM FULL internally create a new table and swap them later.
+VACUUM FULL regtest_table;
+
+CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100;
+
+CREATE SEQUENCE regtest_seq;
+
+CREATE TYPE regtest_comptype AS (a int, b text);
+
+CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END';
+
+CREATE AGGREGATE regtest_agg (
+ sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0'
+);
+
+-- CREATE objects owned by others
+SET SESSION AUTHORIZATION regtest_sepgsql_test_user;
+
+SET search_path = regtest_schema, public;
+
+CREATE TABLE regtest_table_3 (x int, y serial);
+
+CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < y;
+
+CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql
+ AS 'BEGIN RETURN $1 * $1 < 100; END';
+
+RESET SESSION AUTHORIZATION;
+
+--
+-- DROP Permission checks (with clean-up)
+--
+
+DROP FUNCTION regtest_func(text,int[]);
+DROP AGGREGATE regtest_agg(int);
+
+DROP SEQUENCE regtest_seq;
+DROP VIEW regtest_view;
+
+ALTER TABLE regtest_table DROP COLUMN y;
+ALTER TABLE regtest_table_2 SET WITHOUT OIDS;
+
+DROP TABLE regtest_table;
+
+DROP OWNED BY regtest_sepgsql_test_user;
+
+DROP DATABASE regtest_sepgsql_test_database;
+DROP USER regtest_sepgsql_test_user;
+DROP SCHEMA IF EXISTS regtest_schema CASCADE;
diff --git a/contrib/sepgsql/test_sepgsql b/contrib/sepgsql/test_sepgsql
index 52237e6..473004f 100755
--- a/contrib/sepgsql/test_sepgsql
+++ b/contrib/sepgsql/test_sepgsql
@@ -259,6 +259,6 @@ echo "found ${NUM}"
echo
echo "============== running sepgsql regression tests =============="
-make REGRESS="label dml create misc" REGRESS_OPTS="--launcher ./launcher" installcheck
+make REGRESS="label dml ddl misc" REGRESS_OPTS="--launcher ./launcher" installcheck
# exit with the exit code provided by "make"
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index e45c258..d9b1fed 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -448,6 +448,20 @@ UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
</para>
<para>
+ When <literal>DROP</> command is executed, <literal>drop</> will be
+ checked on the object being removed for each object types.
+ Please note that it shall not be checked on the objects removed by
+ cascaded deletion according to the standard manner in SQL.
+ </para>
+ <para>
+ A few additional checks are applied depending on object types.
+ On deletion of objects underlying a particula schema (tables, views,
+ sequences and procedures), <literal>remove_name</> shall be also checked
+ on the schema, not only <literal>drop</> on the object being removed
+ itself.
+ </para>
+
+ <para>
When <xref linkend="sql-security-label"> is executed, <literal>setattr</>
and <literal>relabelfrom</> will be checked on the object being relabeled
with its old security label, then <literal>relabelto</> with the supplied
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index db86262..aa6de11 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -20,6 +20,7 @@
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
@@ -972,6 +973,15 @@ deleteOneObject(const ObjectAddress *object, Relation depRel, int flags)
SysScanDesc scan;
HeapTuple tup;
+ /* DROP hook of the objects being removed */
+ if (object_access_hook)
+ {
+ ObjectAccessDrop drop_arg;
+ drop_arg.dropflags = flags;
+ InvokeObjectAccessHook(OAT_DROP, object->classId, object->objectId,
+ object->objectSubId, &drop_arg);
+ }
+
/*
* 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 aef410a..4e6459c 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1279,7 +1279,8 @@ heap_create_with_catalog(const char *relname,
}
/* Post creation hook for new relation */
- InvokeObjectAccessHook(OAT_POST_CREATE, RelationRelationId, relid, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ RelationRelationId, relid, 0, NULL);
/*
* Store any supplied constraints and defaults.
diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c
index 511d70c..18c7acf 100644
--- a/src/backend/catalog/pg_collation.c
+++ b/src/backend/catalog/pg_collation.c
@@ -136,7 +136,7 @@ CollationCreate(const char *collname, Oid collnamespace,
/* Post creation hook for new collation */
InvokeObjectAccessHook(OAT_POST_CREATE,
- CollationRelationId, oid, 0);
+ CollationRelationId, oid, 0, NULL);
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 0bad4d9..342cf75 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -366,7 +366,8 @@ CreateConstraintEntry(const char *constraintName,
}
/* Post creation hook for new constraint */
- InvokeObjectAccessHook(OAT_POST_CREATE, ConstraintRelationId, conOid, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ ConstraintRelationId, conOid, 0, NULL);
return conOid;
}
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 8194cd6..f86c84f 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -134,8 +134,8 @@ ConversionCreate(const char *conname, Oid connamespace,
recordDependencyOnCurrentExtension(&myself, false);
/* Post creation hook for new conversion */
- InvokeObjectAccessHook(OAT_POST_CREATE,
- ConversionRelationId, HeapTupleGetOid(tup), 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE, ConversionRelationId,
+ HeapTupleGetOid(tup), 0, NULL);
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c
index d5ab48a..da5fe5d 100644
--- a/src/backend/catalog/pg_namespace.c
+++ b/src/backend/catalog/pg_namespace.c
@@ -86,7 +86,8 @@ NamespaceCreate(const char *nspName, Oid ownerId)
recordDependencyOnCurrentExtension(&myself, false);
/* Post creation hook for new schema */
- InvokeObjectAccessHook(OAT_POST_CREATE, NamespaceRelationId, nspoid, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ NamespaceRelationId, nspoid, 0, NULL);
return nspoid;
}
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 3b72722..4fd55ae 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -275,7 +275,7 @@ OperatorShellMake(const char *operatorName,
/* Post creation hook for new shell operator */
InvokeObjectAccessHook(OAT_POST_CREATE,
- OperatorRelationId, operatorObjectId, 0);
+ OperatorRelationId, operatorObjectId, 0, NULL);
/*
* Make sure the tuple is visible for subsequent lookups/updates.
@@ -544,7 +544,7 @@ OperatorCreate(const char *operatorName,
/* Post creation hook for new operator */
InvokeObjectAccessHook(OAT_POST_CREATE,
- OperatorRelationId, operatorObjectId, 0);
+ OperatorRelationId, operatorObjectId, 0, NULL);
heap_close(pg_operator_desc, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 8c0d5f9..b7575a6 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -653,7 +653,8 @@ ProcedureCreate(const char *procedureName,
heap_freetuple(tup);
/* Post creation hook for new function */
- InvokeObjectAccessHook(OAT_POST_CREATE, ProcedureRelationId, retval, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ ProcedureRelationId, retval, 0, NULL);
heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 2c2e3b3..5b2ad6b 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -162,7 +162,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
false);
/* Post creation hook for new shell type */
- InvokeObjectAccessHook(OAT_POST_CREATE, TypeRelationId, typoid, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TypeRelationId, typoid, 0, NULL);
/*
* clean up and return the type-oid
@@ -474,7 +475,8 @@ TypeCreate(Oid newTypeOid,
rebuildDeps);
/* Post creation hook for new type */
- InvokeObjectAccessHook(OAT_POST_CREATE, TypeRelationId, typeObjectId, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TypeRelationId, typeObjectId, 0, NULL);
/*
* finish up
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 42a8b31..91d7481 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -515,7 +515,8 @@ createdb(const CreatedbStmt *stmt)
copyTemplateDependencies(src_dboid, dboid);
/* Post creation hook for new database */
- InvokeObjectAccessHook(OAT_POST_CREATE, DatabaseRelationId, dboid, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ DatabaseRelationId, dboid, 0, NULL);
/*
* Force a checkpoint before starting the copy. This will force dirty
@@ -777,6 +778,15 @@ dropdb(const char *dbname, bool missing_ok)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
dbname);
+ /* DROP hook for the database being removed */
+ if (object_access_hook)
+ {
+ ObjectAccessDrop drop_arg;
+ memset(&drop_arg, 0, sizeof(ObjectAccessDrop));
+ InvokeObjectAccessHook(OAT_DROP,
+ DatabaseRelationId, db_id, 0, &drop_arg);
+ }
+
/*
* Disallow dropping a DB that is marked istemplate. This is just to
* prevent people from accidentally dropping template0 or template1; they
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 6ecbbc7..64fca87 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1558,7 +1558,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
}
/* Post creation hook for new extension */
InvokeObjectAccessHook(OAT_POST_CREATE,
- ExtensionRelationId, extensionOid, 0);
+ ExtensionRelationId, extensionOid, 0, NULL);
return extensionOid;
}
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 4135e26..61e9b6c 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -594,7 +594,7 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
/* Post creation hook for new foreign data wrapper */
InvokeObjectAccessHook(OAT_POST_CREATE,
- ForeignDataWrapperRelationId, fdwId, 0);
+ ForeignDataWrapperRelationId, fdwId, 0, NULL);
heap_close(rel, RowExclusiveLock);
}
@@ -890,7 +890,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
recordDependencyOnCurrentExtension(&myself, false);
/* Post creation hook for new foreign server */
- InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ ForeignServerRelationId, srvId, 0, NULL);
heap_close(rel, RowExclusiveLock);
}
@@ -1130,7 +1131,8 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
recordDependencyOnCurrentExtension(&myself, false);
/* Post creation hook for new user mapping */
- InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ UserMappingRelationId, umId, 0, NULL);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index c692a65..4c40a73 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1723,7 +1723,8 @@ CreateCast(CreateCastStmt *stmt)
recordDependencyOnCurrentExtension(&myself, false);
/* Post creation hook for new cast */
- InvokeObjectAccessHook(OAT_POST_CREATE, CastRelationId, castid, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ CastRelationId, castid, 0, NULL);
heap_freetuple(tuple);
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 5dc131a..87c8896 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -314,7 +314,7 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
/* Post creation hook for new operator family */
InvokeObjectAccessHook(OAT_POST_CREATE,
- OperatorFamilyRelationId, opfamilyoid, 0);
+ OperatorFamilyRelationId, opfamilyoid, 0, NULL);
heap_close(rel, RowExclusiveLock);
@@ -717,7 +717,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
/* Post creation hook for new operator class */
InvokeObjectAccessHook(OAT_POST_CREATE,
- OperatorClassRelationId, opclassoid, 0);
+ OperatorClassRelationId, opclassoid, 0, NULL);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index a3a111d..87cf2b3 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -425,7 +425,7 @@ create_proc_lang(const char *languageName, bool replace,
/* Post creation hook for new procedural language */
InvokeObjectAccessHook(OAT_POST_CREATE,
- LanguageRelationId, myself.objectId, 0);
+ LanguageRelationId, myself.objectId, 0, NULL);
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 07dc326..10bb48b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4379,7 +4379,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* Post creation hook for new attribute */
InvokeObjectAccessHook(OAT_POST_CREATE,
- RelationRelationId, myrelid, newattnum);
+ RelationRelationId, myrelid, newattnum, NULL);
heap_close(pgclass, RowExclusiveLock);
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 8ed6d06..8cd836d 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -330,7 +330,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
/* Post creation hook for new tablespace */
InvokeObjectAccessHook(OAT_POST_CREATE,
- TableSpaceRelationId, tablespaceoid, 0);
+ TableSpaceRelationId, tablespaceoid, 0, NULL);
create_tablespace_directories(location, tablespaceoid);
@@ -434,6 +434,15 @@ DropTableSpace(DropTableSpaceStmt *stmt)
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE,
tablespacename);
+ /* DROP hook for the tablespace being removed */
+ if (object_access_hook)
+ {
+ ObjectAccessDrop drop_arg;
+ memset(&drop_arg, 0, sizeof(ObjectAccessDrop));
+ InvokeObjectAccessHook(OAT_DROP, TableSpaceRelationId,
+ tablespaceoid, 0, &drop_arg);
+ }
+
/*
* Remove the pg_tablespace tuple (this will roll back if we fail below)
*/
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 2838b66..4dc5732 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -749,7 +749,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
/* Post creation hook for new trigger */
InvokeObjectAccessHook(OAT_POST_CREATE,
- TriggerRelationId, trigoid, 0);
+ TriggerRelationId, trigoid, 0, NULL);
/* Keep lock on target rel until end of xact */
heap_close(rel, NoLock);
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index fe500a6..86cb870 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -271,7 +271,8 @@ DefineTSParser(List *names, List *parameters)
makeParserDependencies(tup);
/* Post creation hook for new text search parser */
- InvokeObjectAccessHook(OAT_POST_CREATE, TSParserRelationId, prsOid, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TSParserRelationId, prsOid, 0, NULL);
heap_freetuple(tup);
@@ -565,7 +566,7 @@ DefineTSDictionary(List *names, List *parameters)
/* Post creation hook for new text search dictionary */
InvokeObjectAccessHook(OAT_POST_CREATE,
- TSDictionaryRelationId, dictOid, 0);
+ TSDictionaryRelationId, dictOid, 0, NULL);
heap_freetuple(tup);
@@ -1036,7 +1037,8 @@ DefineTSTemplate(List *names, List *parameters)
makeTSTemplateDependencies(tup);
/* Post creation hook for new text search template */
- InvokeObjectAccessHook(OAT_POST_CREATE, TSTemplateRelationId, dictOid, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TSTemplateRelationId, dictOid, 0, NULL);
heap_freetuple(tup);
@@ -1419,7 +1421,8 @@ DefineTSConfiguration(List *names, List *parameters)
makeConfigurationDependencies(tup, false, mapRel);
/* Post creation hook for new text search configuration */
- InvokeObjectAccessHook(OAT_POST_CREATE, TSConfigRelationId, cfgOid, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TSConfigRelationId, cfgOid, 0, NULL);
heap_freetuple(tup);
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 9a88c90..2edbabe 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -425,7 +425,8 @@ CreateRole(CreateRoleStmt *stmt)
GetUserId(), false);
/* Post creation hook for new role */
- InvokeObjectAccessHook(OAT_POST_CREATE, AuthIdRelationId, roleid, 0);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ AuthIdRelationId, roleid, 0, NULL);
/*
* Close pg_authid, but keep lock till commit.
@@ -932,6 +933,15 @@ DropRole(DropRoleStmt *stmt)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to drop superusers")));
+ /* DROP hook for the role being removed */
+ if (object_access_hook)
+ {
+ ObjectAccessDrop drop_arg;
+ memset(&drop_arg, 0, sizeof(ObjectAccessDrop));
+ InvokeObjectAccessHook(OAT_DROP,
+ AuthIdRelationId, roleid, 0, &drop_arg);
+ }
+
/*
* Lock the role, so nobody can add dependencies to her while we drop
* her. We keep the lock until the end of transaction.
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 8c87ac5..645182d 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -178,7 +178,7 @@ InsertRule(char *rulname,
/* Post creation hook for new rule */
InvokeObjectAccessHook(OAT_POST_CREATE,
- RewriteRelationId, rewriteObjectId, 0);
+ RewriteRelationId, rewriteObjectId, 0, NULL);
heap_close(pg_rewrite_desc, RowExclusiveLock);
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index a14ce44..3adfb15 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -217,7 +217,7 @@ inv_create(Oid lobjId)
/* Post creation hook for new large object */
InvokeObjectAccessHook(OAT_POST_CREATE,
- LargeObjectRelationId, lobjId_new, 0);
+ LargeObjectRelationId, lobjId_new, 0, NULL);
/*
* Advance command counter to make new tuple visible to later operations.
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 5c7a40a..9763280 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -19,28 +19,45 @@
* Typically, this is done after inserting the primary catalog records and
* associated dependencies.
*
+ * OAT_DROP should be invoked just before deletion of objects; typically
+ * deleteOneObject(). Its arguments are packed within ObjectAccessDrop.
+ *
* Other types may be added in the future.
*/
typedef enum ObjectAccessType
{
OAT_POST_CREATE,
+ OAT_DROP,
} ObjectAccessType;
/*
- * Hook, and a macro to invoke it.
+ * Arguments of OAT_DROP event
*/
+typedef struct
+{
+ /*
+ * Flags to inform extensions the context of this deletion.
+ * Also see PERFORM_DELETION_* in dependency.h
+ */
+ int dropflags;
+} ObjectAccessDrop;
+/*
+ * Hook, and a macro to invoke it.
+ */
typedef void (*object_access_hook_type) (ObjectAccessType access,
Oid classId,
Oid objectId,
- int subId);
+ int subId,
+ void *arg);
extern PGDLLIMPORT object_access_hook_type object_access_hook;
-#define InvokeObjectAccessHook(access,classId,objectId,subId) \
- do { \
- if (object_access_hook) \
- (*object_access_hook)((access),(classId),(objectId),(subId)); \
+#define InvokeObjectAccessHook(access,classId,objectId,subId,arg) \
+ do { \
+ if (object_access_hook) \
+ (*object_access_hook)((access),(classId), \
+ (objectId),(subId),(arg)); \
} while(0)
#endif /* OBJECTACCESS_H */
On Sat, Feb 4, 2012 at 10:54 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
OK, I modified the patch according to your suggestions.
object_access_hook was extended to take an argument of void * pointer,
and InvokeObjectAccessHook was also allows to deliver it.
Sorry for the long radio silence on this patch. The changes to the
object-access hook stuff seem sound to me now, so I've gone ahead and
committed that part of this. I'll look at the rest next.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Fri, Mar 9, 2012 at 2:41 PM, Robert Haas <robertmhaas@gmail.com> wrote:
On Sat, Feb 4, 2012 at 10:54 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
OK, I modified the patch according to your suggestions.
object_access_hook was extended to take an argument of void * pointer,
and InvokeObjectAccessHook was also allows to deliver it.Sorry for the long radio silence on this patch. The changes to the
object-access hook stuff seem sound to me now, so I've gone ahead and
committed that part of this. I'll look at the rest next.
That looks fine also, so committed that too.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company