From e965e6344cfaff0708a032721b56f61eea777bc5 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Fri, 5 Apr 2013 08:51:31 -0400 Subject: [PATCH] sepgsql: Enforce db_schema:search permission. KaiGai Kohei, with comment and doc wordsmithing by me --- contrib/sepgsql/expected/alter.out | 31 ++++++++++++++++++ contrib/sepgsql/expected/ddl.out | 52 ++++++++++++++++++++++++++++++ contrib/sepgsql/expected/dml.out | 27 ++++++++++++++++ contrib/sepgsql/hooks.c | 19 +++++++++++ contrib/sepgsql/schema.c | 35 +++++++++++++------- contrib/sepgsql/sepgsql-regtest.te | 5 ++- contrib/sepgsql/sepgsql.h | 1 + contrib/sepgsql/sql/dml.sql | 18 +++++++++++ doc/src/sgml/sepgsql.sgml | 10 ++++++ src/backend/catalog/namespace.c | 15 +++++++-- src/backend/catalog/objectaccess.c | 25 ++++++++++++++ src/backend/tcop/fastpath.c | 2 ++ src/include/catalog/objectaccess.h | 33 +++++++++++++++++++ 13 files changed, 258 insertions(+), 15 deletions(-) diff --git a/contrib/sepgsql/expected/alter.out b/contrib/sepgsql/expected/alter.out index ef9abb34ce..718aced0cc 100644 --- a/contrib/sepgsql/expected/alter.out +++ b/contrib/sepgsql/expected/alter.out @@ -48,6 +48,9 @@ LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined ALTER SCHEMA regtest_schema_1 OWNER TO regtest_sepgsql_test_user; LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_1" ALTER TABLE regtest_table_1 OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_1" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema public" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_1" ALTER TABLE regtest_table_1 OWNER TO regtest_sepgsql_test_user; LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_1" @@ -90,6 +93,8 @@ LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined ALTER SCHEMA regtest_schema_1 RENAME TO regtest_schema; LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_1" ALTER TABLE regtest_table_1 RENAME TO regtest_table; +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema public" LOG: SELinux: allowed { add_name 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_2" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_1" ALTER SEQUENCE regtest_seq_1 RENAME TO regtest_seq; @@ -109,7 +114,13 @@ ALTER DATABASE regtest_sepgsql_test_database CONNECTION LIMIT 999; LOG: SELinux: allowed { setattr } 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" ALTER DATABASE regtest_sepgsql_test_database SET search_path TO regtest_schema, public; -- not supported yet ALTER TABLE regtest_table ADD COLUMN d float; +LOG: SELinux: allowed { search } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema public" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" 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 d" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" 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 d" ALTER TABLE regtest_table DROP COLUMN d; 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 d" @@ -132,10 +143,30 @@ ALTER TABLE regtest_table ALTER b SET STORAGE PLAIN; LOG: SELinux: allowed { setattr } 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 b" LOG: SELinux: allowed { setattr } 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" ALTER TABLE regtest_table ADD CONSTRAINT test_fk FOREIGN KEY (a) REFERENCES regtest_table_3(x); -- not supported +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" LOG: SELinux: allowed { select } 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 { select } 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 a" LOG: SELinux: allowed { select } 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 { select } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LINE 1: SELECT fk."a" FROM ONLY "regtest_schema_2"."regtest_table" f... + ^ +QUERY: SELECT fk."a" FROM ONLY "regtest_schema_2"."regtest_table" fk LEFT OUTER JOIN ONLY "regtest_schema"."regtest_table_3" pk ON ( pk."x" OPERATOR(pg_catalog.=) fk."a") WHERE pk."x" IS NULL AND (fk."a" IS NOT NULL) +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LINE 1: ...schema_2"."regtest_table" fk LEFT OUTER JOIN ONLY "regtest_s... + ^ +QUERY: SELECT fk."a" FROM ONLY "regtest_schema_2"."regtest_table" fk LEFT OUTER JOIN ONLY "regtest_schema"."regtest_table_3" pk ON ( pk."x" OPERATOR(pg_catalog.=) fk."a") WHERE pk."x" IS NULL AND (fk."a" IS NOT NULL) +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +CONTEXT: SQL statement "SELECT fk."a" FROM ONLY "regtest_schema_2"."regtest_table" fk LEFT OUTER JOIN ONLY "regtest_schema"."regtest_table_3" pk ON ( pk."x" OPERATOR(pg_catalog.=) fk."a") WHERE pk."x" IS NULL AND (fk."a" IS NOT NULL)" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +CONTEXT: SQL statement "SELECT fk."a" FROM ONLY "regtest_schema_2"."regtest_table" fk LEFT OUTER JOIN ONLY "regtest_schema"."regtest_table_3" pk ON ( pk."x" OPERATOR(pg_catalog.=) fk."a") WHERE pk."x" IS NULL AND (fk."a" IS NOT NULL)" LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table" CONTEXT: SQL statement "SELECT fk."a" FROM ONLY "regtest_schema_2"."regtest_table" fk LEFT OUTER JOIN ONLY "regtest_schema"."regtest_table_3" pk ON ( pk."x" OPERATOR(pg_catalog.=) fk."a") WHERE pk."x" IS NULL AND (fk."a" IS NOT NULL)" LOG: SELinux: allowed { select } 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 a" diff --git a/contrib/sepgsql/expected/ddl.out b/contrib/sepgsql/expected/ddl.out index d60c65bdb1..b578b9fe10 100644 --- a/contrib/sepgsql/expected/ddl.out +++ b/contrib/sepgsql/expected/ddl.out @@ -24,9 +24,12 @@ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_ 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" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema public" 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); +LOG: SELinux: allowed { search } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema public" 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" @@ -39,12 +42,27 @@ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column ctid" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column x" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column y" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } 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 { search } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LINE 1: CREATE TABLE regtest_table (x serial primary key, y text); + ^ +LOG: SELinux: allowed { search } 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 { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table" +LOG: SELinux: allowed { search } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq" ALTER TABLE regtest_table ADD COLUMN z int; +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" 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" @@ -67,9 +85,11 @@ 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); +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" 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" CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END'; +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" 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 ( @@ -81,8 +101,12 @@ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_ SET SESSION AUTHORIZATION regtest_sepgsql_test_user; SET search_path = regtest_schema, public; CREATE TABLE regtest_table_3 (x int, y serial); +LOG: SELinux: allowed { search } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema public" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" 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" @@ -93,12 +117,18 @@ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_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" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_3_y_seq" 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" 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; @@ -106,6 +136,14 @@ RESET SESSION AUTHORIZATION; -- ALTER and CREATE/DROP extra attribute permissions -- CREATE TABLE regtest_table_4 (x int primary key, y int, z int); +LOG: SELinux: allowed { search } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema public" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column tableoid" @@ -117,6 +155,12 @@ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column x" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column y" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column z" +LOG: SELinux: allowed { search } 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 { search } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" +LINE 1: CREATE TABLE regtest_table_4 (x int primary key, y int, z in... + ^ +LOG: SELinux: allowed { search } 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 { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" CREATE INDEX regtest_index_tbl4_y ON regtest_table_4(y); @@ -126,6 +170,8 @@ CREATE INDEX regtest_index_tbl4_z ON regtest_table_4(z); LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" ALTER TABLE regtest_table_4 ALTER COLUMN y TYPE float; +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column y" DROP INDEX regtest_index_tbl4_y; LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" @@ -156,9 +202,11 @@ LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t: -- DROP Permission checks (with clean-up) -- DROP FUNCTION regtest_func(text,int[]); +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema pg_catalog" 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; @@ -187,6 +235,8 @@ LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t: 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 { search } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema public" 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" @@ -207,6 +257,8 @@ 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 { search } 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 { search } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema public" 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/expected/dml.out b/contrib/sepgsql/expected/dml.out index 1a29a63971..3b90f89347 100644 --- a/contrib/sepgsql/expected/dml.out +++ b/contrib/sepgsql/expected/dml.out @@ -47,6 +47,12 @@ ORDER BY objname; column | t5.g | system_u:object_r:sepgsql_secret_table_t:s0 (8 rows) +CREATE SCHEMA my_schema_1; +CREATE TABLE my_schema_1.ts1 (a int, b text); +CREATE SCHEMA my_schema_2; +CREATE TABLE my_schema_2.ts2 (x int, y text); +SECURITY LABEL ON SCHEMA my_schema_2 + IS 'system_u:object_r:sepgsql_regtest_invisible_schema_t:s0'; -- Hardwired Rules UPDATE pg_attribute SET attisdropped = true WHERE attrelid = 't5'::regclass AND attname = 'f'; -- failed @@ -166,6 +172,23 @@ COPY t5 (e,f) FROM '/dev/null'; -- failed ERROR: SELinux: security policy violation COPY t5 (e) FROM '/dev/null'; -- ok -- +-- Schema search path +-- +SET search_path = my_schema_1, my_schema_2, public; +SELECT * FROM ts1; -- ok + a | b +---+--- +(0 rows) + +SELECT * FROM ts2; -- failed (relation not found) +ERROR: relation "ts2" does not exist +LINE 1: SELECT * FROM ts2; + ^ +SELECT * FROM my_schema_2.ts2; -- failed (policy violation) +ERROR: SELinux: security policy violation +LINE 1: SELECT * FROM my_schema_2.ts2; + ^ +-- -- Clean up -- SELECT sepgsql_getcon(); -- confirm client privilege @@ -180,3 +203,7 @@ DROP TABLE IF EXISTS t3 CASCADE; DROP TABLE IF EXISTS t4 CASCADE; DROP TABLE IF EXISTS t5 CASCADE; DROP TABLE IF EXISTS customer CASCADE; +DROP SCHEMA IF EXISTS my_schema_1 CASCADE; +NOTICE: drop cascades to table my_schema_1.ts1 +DROP SCHEMA IF EXISTS my_schema_2 CASCADE; +NOTICE: drop cascades to table my_schema_2.ts2 diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 0715aa8bc6..5faa1ea797 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -236,6 +236,25 @@ sepgsql_object_access(ObjectAccessType access, } break; + case OAT_NAMESPACE_SEARCH: + { + ObjectAccessNamespaceSearch *ns_arg = arg; + + /* + * If stacked extension already decided not to allow users + * to search this schema, we just stick with that decision. + */ + if (!ns_arg->result) + break; + + Assert(classId == NamespaceRelationId); + Assert(ns_arg->result); + ns_arg->result + = sepgsql_schema_search(objectId, + ns_arg->ereport_on_violation); + } + break; + default: elog(ERROR, "unexpected object access type: %d", (int) access); break; diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c index 74e16678cb..bafe17adcd 100644 --- a/contrib/sepgsql/schema.c +++ b/contrib/sepgsql/schema.c @@ -173,42 +173,54 @@ sepgsql_schema_relabel(Oid namespaceId, const char *seclabel) * * utility routine to check db_schema:{xxx} permissions */ -static void -check_schema_perms(Oid namespaceId, uint32 required) +static bool +check_schema_perms(Oid namespaceId, uint32 required, bool abort_on_violation) { ObjectAddress object; char *audit_name; + bool result; object.classId = NamespaceRelationId; object.objectId = namespaceId; object.objectSubId = 0; audit_name = getObjectDescription(&object); - sepgsql_avc_check_perms(&object, - SEPG_CLASS_DB_SCHEMA, - required, - audit_name, - true); + result = sepgsql_avc_check_perms(&object, + SEPG_CLASS_DB_SCHEMA, + required, + audit_name, + abort_on_violation); pfree(audit_name); + + return result; } /* db_schema:{setattr} permission */ void sepgsql_schema_setattr(Oid namespaceId) { - check_schema_perms(namespaceId, SEPG_DB_SCHEMA__SETATTR); + check_schema_perms(namespaceId, SEPG_DB_SCHEMA__SETATTR, true); +} + +/* db_schema:{search} permission */ +bool +sepgsql_schema_search(Oid namespaceId, bool abort_on_violation) +{ + return check_schema_perms(namespaceId, + SEPG_DB_SCHEMA__SEARCH, + abort_on_violation); } void sepgsql_schema_add_name(Oid namespaceId) { - check_schema_perms(namespaceId, SEPG_DB_SCHEMA__ADD_NAME); + check_schema_perms(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); } void sepgsql_schema_remove_name(Oid namespaceId) { - check_schema_perms(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME); + check_schema_perms(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); } void @@ -216,5 +228,6 @@ sepgsql_schema_rename(Oid namespaceId) { check_schema_perms(namespaceId, SEPG_DB_SCHEMA__ADD_NAME | - SEPG_DB_SCHEMA__REMOVE_NAME); + SEPG_DB_SCHEMA__REMOVE_NAME, + true); } diff --git a/contrib/sepgsql/sepgsql-regtest.te b/contrib/sepgsql/sepgsql-regtest.te index 790c4e85bb..823384ecc6 100644 --- a/contrib/sepgsql/sepgsql-regtest.te +++ b/contrib/sepgsql/sepgsql-regtest.te @@ -1,4 +1,4 @@ -policy_module(sepgsql-regtest, 1.05) +policy_module(sepgsql-regtest, 1.06) gen_require(` all_userspace_class_perms @@ -20,6 +20,9 @@ postgresql_procedure_object(sepgsql_regtest_trusted_proc_exec_t) type sepgsql_nosuch_trusted_proc_exec_t; postgresql_procedure_object(sepgsql_nosuch_trusted_proc_exec_t) +type sepgsql_regtest_invisible_schema_t; +postgresql_schema_object(sepgsql_regtest_invisible_schema_t); + # # Test domains for database administrators # diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index f7448cdeb6..37d7455fd2 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -303,6 +303,7 @@ 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); extern void sepgsql_schema_setattr(Oid namespaceId); +extern bool sepgsql_schema_search(Oid namespaceId, bool abort_on_violation); extern void sepgsql_schema_add_name(Oid namespaceId); extern void sepgsql_schema_remove_name(Oid namespaceId); extern void sepgsql_schema_rename(Oid namespaceId); diff --git a/contrib/sepgsql/sql/dml.sql b/contrib/sepgsql/sql/dml.sql index 94bf31a97a..97e01c3e3c 100644 --- a/contrib/sepgsql/sql/dml.sql +++ b/contrib/sepgsql/sql/dml.sql @@ -43,6 +43,14 @@ SELECT objtype, objname, label FROM pg_seclabels AND objname in ('t1', 't2', 't3', 't4', 't5', 't5.e', 't5.f', 't5.g') ORDER BY objname; +CREATE SCHEMA my_schema_1; +CREATE TABLE my_schema_1.ts1 (a int, b text); +CREATE SCHEMA my_schema_2; +CREATE TABLE my_schema_2.ts2 (x int, y text); + +SECURITY LABEL ON SCHEMA my_schema_2 + IS 'system_u:object_r:sepgsql_regtest_invisible_schema_t:s0'; + -- Hardwired Rules UPDATE pg_attribute SET attisdropped = true WHERE attrelid = 't5'::regclass AND attname = 'f'; -- failed @@ -107,6 +115,14 @@ COPY t5 FROM '/dev/null'; -- failed COPY t5 (e,f) FROM '/dev/null'; -- failed COPY t5 (e) FROM '/dev/null'; -- ok +-- +-- Schema search path +-- +SET search_path = my_schema_1, my_schema_2, public; +SELECT * FROM ts1; -- ok +SELECT * FROM ts2; -- failed (relation not found) +SELECT * FROM my_schema_2.ts2; -- failed (policy violation) + -- -- Clean up -- @@ -117,3 +133,5 @@ DROP TABLE IF EXISTS t3 CASCADE; DROP TABLE IF EXISTS t4 CASCADE; DROP TABLE IF EXISTS t5 CASCADE; DROP TABLE IF EXISTS customer CASCADE; +DROP SCHEMA IF EXISTS my_schema_1 CASCADE; +DROP SCHEMA IF EXISTS my_schema_2 CASCADE; diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml index da0915bff3..0a2ee86a11 100644 --- a/doc/src/sgml/sepgsql.sgml +++ b/doc/src/sgml/sepgsql.sgml @@ -397,6 +397,16 @@ UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100; checked in this version. + + In order to access any schema object, db_schema:search + permission is required on the containing schema. When an object is + referenced without schema qualification, schemas on which this + permission is not present will not be searched (just as if the user did + not have USAGE privilege on the schema). If an explicit schema + qualification is present, an error will occur if the user does not have + the requisite permission on the named schema. + + The client must be allowed to access all referenced tables and columns, even if they originated from views which were then expanded, diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 07a8761709..f48c0bcb31 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -22,6 +22,7 @@ #include "access/htup_details.h" #include "access/xact.h" #include "catalog/dependency.h" +#include "catalog/objectaccess.h" #include "catalog/pg_authid.h" #include "catalog/pg_collation.h" #include "catalog/pg_conversion.h" @@ -2655,7 +2656,10 @@ LookupNamespaceNoError(const char *nspname) if (strcmp(nspname, "pg_temp") == 0) { if (OidIsValid(myTempNamespace)) + { + InvokeNamespaceSearchHook(myTempNamespace, true); return myTempNamespace; + } /* * Since this is used only for looking up existing objects, there is @@ -2702,6 +2706,8 @@ LookupExplicitNamespace(const char *nspname, bool missing_ok) if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, nspname); + /* Schema search hook for this lookup */ + InvokeNamespaceSearchHook(namespaceId, true); return namespaceId; } @@ -3468,7 +3474,8 @@ recomputeNamespacePath(void) if (OidIsValid(namespaceId) && !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, - ACL_USAGE) == ACLCHECK_OK) + ACL_USAGE) == ACLCHECK_OK && + InvokeNamespaceSearchHook(namespaceId, false)) oidlist = lappend_oid(oidlist, namespaceId); } } @@ -3477,7 +3484,8 @@ recomputeNamespacePath(void) /* pg_temp --- substitute temp namespace, if any */ if (OidIsValid(myTempNamespace)) { - if (!list_member_oid(oidlist, myTempNamespace)) + if (!list_member_oid(oidlist, myTempNamespace) && + InvokeNamespaceSearchHook(myTempNamespace, false)) oidlist = lappend_oid(oidlist, myTempNamespace); } else @@ -3494,7 +3502,8 @@ recomputeNamespacePath(void) if (OidIsValid(namespaceId) && !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, - ACL_USAGE) == ACLCHECK_OK) + ACL_USAGE) == ACLCHECK_OK && + InvokeNamespaceSearchHook(namespaceId, false)) oidlist = lappend_oid(oidlist, namespaceId); } } diff --git a/src/backend/catalog/objectaccess.c b/src/backend/catalog/objectaccess.c index bc565ebe49..f70797f264 100644 --- a/src/backend/catalog/objectaccess.c +++ b/src/backend/catalog/objectaccess.c @@ -11,6 +11,7 @@ #include "postgres.h" #include "catalog/objectaccess.h" +#include "catalog/pg_namespace.h" /* * Hook on object accesses. This is intended as infrastructure for security @@ -84,3 +85,27 @@ RunObjectPostAlterHook(Oid classId, Oid objectId, int subId, classId, objectId, subId, (void *) &pa_arg); } + +/* + * RunNamespaceSearchHook + * + * It is entrypoint of OAT_NAMESPACE_SEARCH event + */ +bool +RunNamespaceSearchHook(Oid objectId, bool ereport_on_violation) +{ + ObjectAccessNamespaceSearch ns_arg; + + /* XXX - should be checked at caller side */ + Assert(object_access_hook != NULL); + + memset(&ns_arg, 0, sizeof(ObjectAccessNamespaceSearch)); + ns_arg.ereport_on_violation = ereport_on_violation; + ns_arg.result = true; + + (*object_access_hook)(OAT_NAMESPACE_SEARCH, + NamespaceRelationId, objectId, 0, + (void *) &ns_arg); + + return ns_arg.result; +} diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c index 21c979753e..016e7d9ec6 100644 --- a/src/backend/tcop/fastpath.c +++ b/src/backend/tcop/fastpath.c @@ -22,6 +22,7 @@ #include "access/htup_details.h" #include "access/xact.h" +#include "catalog/objectaccess.h" #include "catalog/pg_proc.h" #include "libpq/libpq.h" #include "libpq/pqformat.h" @@ -355,6 +356,7 @@ HandleFunctionRequest(StringInfo msgBuf) if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(fip->namespace)); + InvokeNamespaceSearchHook(fip->namespace, true); aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h index 25f963b074..12ae55f498 100644 --- a/src/include/catalog/objectaccess.h +++ b/src/include/catalog/objectaccess.h @@ -27,6 +27,10 @@ * hook can use SnapshotNow and SnapshotSelf to get the old and new * versions of the tuple. * + * OAT_NAMESPACE_SEARCH should be invoked prior to object name lookup under + * a particular namespace. This event is equivalent to usage permission + * permission on a schema under the default access control mechanism. + * * Other types may be added in the future. */ typedef enum ObjectAccessType @@ -34,6 +38,7 @@ typedef enum ObjectAccessType OAT_POST_CREATE, OAT_DROP, OAT_POST_ALTER, + OAT_NAMESPACE_SEARCH, } ObjectAccessType; /* @@ -84,6 +89,28 @@ typedef struct bool is_internal; } ObjectAccessPostAlter; +/* + * Arguments of OAT_NAMESPACE_SEARCH + */ +typedef struct +{ + /* + * If true, hook should report an error when permission to search this + * schema is denied. + */ + bool ereport_on_violation; + + /* + * This is, in essence, an out parameter. Core code should + * initialize this to true, and any extension that wants to deny + * access should reset it to false. But an extension should be + * careful never to store a true value here, so that in case there are + * multiple extensions access is only allowed if all extensions + * agree. + */ + bool result; +} ObjectAccessNamespaceSearch; + /* Plugin provides a hook function matching this signature. */ typedef void (*object_access_hook_type) (ObjectAccessType access, Oid classId, @@ -101,6 +128,7 @@ extern void RunObjectDropHook(Oid classId, Oid objectId, int subId, int dropflags); extern void RunObjectPostAlterHook(Oid classId, Oid objectId, int subId, Oid auxiliaryId, bool is_internal); +extern bool RunNamespaceSearchHook(Oid objectId, bool ereport_on_volation); /* * The following macros are wrappers around the functions above; these should @@ -137,4 +165,9 @@ extern void RunObjectPostAlterHook(Oid classId, Oid objectId, int subId, (auxiliaryId),(is_internal)); \ } while(0) +#define InvokeNamespaceSearchHook(objectId, ereport_on_violation) \ + (!object_access_hook \ + ? true \ + : RunNamespaceSearchHook((objectId), (ereport_on_violation))) + #endif /* OBJECTACCESS_H */