On 07/22/2015 02:17 PM, Dean Rasheed wrote: > On 21 July 2015 at 04:53, Michael Paquier <michael.paqu...@gmail.com> wrote: >> On Tue, Jul 14, 2015 at 4:01 AM, Stephen Frost <sfr...@snowman.net> wrote: >>> We need to be careful to avoid the slippery slope of trying to prevent >>> all covert channels, which has been extensively discussed previously. > > I think this is more serious than the covert channel leaks discussed > before, since most_common_vals explicitly reveals values from the > table, making it an overt leak, albeit of a small portion of the > table's values. > >> Looking at that I am not seeing any straight-forward way to resolve >> this issue except by hardening pg_stats by having an additional filter >> of this type so as a non-owner of a relation cannot see the stats of >> this table directly when RLS is enabled: >> c.relrowsecurity = false OR c.relowner = current_user::regrole::oid >> Attached is a patch doing that (/me now hides, expecting to receive >> laser shots because of the use of current_user on a system view). >> Thoughts? > > Hmm, I think it probably ought to do more, based on whether or not RLS > is being bypassed or in force-mode -- see the first few checks in > get_row_security_policies(). Perhaps a new SQL-callable function > exposing those checks and calling check_enable_rls(). It's probably > still worth including the "c.relrowsecurity = false" check in SQL to > save calling the function for the majority of tables that don't have > RLS.
Please see the attached patch and let me know what you think. I believe the only thing lacking is documentation for the two new user visible functions. Comments? Joe
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index e82a53a..c0bd6fa 100644 *** a/src/backend/catalog/system_views.sql --- b/src/backend/catalog/system_views.sql *************** CREATE VIEW pg_indexes AS *** 150,156 **** LEFT JOIN pg_tablespace T ON (T.oid = I.reltablespace) WHERE C.relkind IN ('r', 'm') AND I.relkind = 'i'; ! CREATE VIEW pg_stats AS SELECT nspname AS schemaname, relname AS tablename, --- 150,156 ---- LEFT JOIN pg_tablespace T ON (T.oid = I.reltablespace) WHERE C.relkind IN ('r', 'm') AND I.relkind = 'i'; ! CREATE VIEW pg_stats WITH (security_barrier) AS SELECT nspname AS schemaname, relname AS tablename, *************** CREATE VIEW pg_stats AS *** 211,217 **** FROM pg_statistic s JOIN pg_class c ON (c.oid = s.starelid) JOIN pg_attribute a ON (c.oid = attrelid AND attnum = s.staattnum) LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) ! WHERE NOT attisdropped AND has_column_privilege(c.oid, a.attnum, 'select'); REVOKE ALL on pg_statistic FROM public; --- 211,219 ---- FROM pg_statistic s JOIN pg_class c ON (c.oid = s.starelid) JOIN pg_attribute a ON (c.oid = attrelid AND attnum = s.staattnum) LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) ! WHERE NOT attisdropped ! AND has_column_privilege(c.oid, a.attnum, 'select') ! AND (c.relrowsecurity = false OR NOT row_security_active(c.oid)); REVOKE ALL on pg_statistic FROM public; diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 3ca168b..094ac33 100644 *** a/src/backend/utils/adt/acl.c --- b/src/backend/utils/adt/acl.c *************** static AclMode convert_priv_string(text *** 92,98 **** static AclMode convert_any_priv_string(text *priv_type_text, const priv_map *privileges); - static Oid convert_table_name(text *tablename); static AclMode convert_table_priv_string(text *priv_type_text); static AclMode convert_sequence_priv_string(text *priv_type_text); static AttrNumber convert_column_name(Oid tableoid, text *column); --- 92,97 ---- *************** has_table_privilege_id_id(PG_FUNCTION_AR *** 1998,2004 **** /* * Given a table name expressed as a string, look it up and return Oid */ ! static Oid convert_table_name(text *tablename) { RangeVar *relrv; --- 1997,2003 ---- /* * Given a table name expressed as a string, look it up and return Oid */ ! Oid convert_table_name(text *tablename) { RangeVar *relrv; diff --git a/src/backend/utils/misc/rls.c b/src/backend/utils/misc/rls.c index 44cb374..cd1a521 100644 *** a/src/backend/utils/misc/rls.c --- b/src/backend/utils/misc/rls.c *************** *** 19,24 **** --- 19,25 ---- #include "catalog/pg_class.h" #include "miscadmin.h" #include "utils/acl.h" + #include "utils/builtins.h" #include "utils/elog.h" #include "utils/rls.h" #include "utils/syscache.h" *************** check_enable_rls(Oid relid, Oid checkAsU *** 111,113 **** --- 112,141 ---- /* RLS should be fully enabled for this relation. */ return RLS_ENABLED; } + + /* + * row_security_active variants + * + * check_enable_rls wrapped as a SQL callable function except + * RLS_NONE_ENV and RLS_NONE are the same for this purpose. + */ + Datum + row_security_active_name(PG_FUNCTION_ARGS) + { + text *tablename = PG_GETARG_TEXT_P(0); + Oid tableoid = convert_table_name(tablename); + + if (check_enable_rls(tableoid, GetUserId(), true) == RLS_ENABLED) + return true; + else + return false; + } + + Datum + row_security_active_id(PG_FUNCTION_ARGS) + { + if (check_enable_rls(PG_GETARG_OID(0), GetUserId(), true) == RLS_ENABLED) + return true; + else + return false; + } diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 8f6685f..5fe1930 100644 *** a/src/include/catalog/catversion.h --- b/src/include/catalog/catversion.h *************** *** 53,58 **** */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 201507171 #endif --- 53,58 ---- */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 201507250 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 1d68ad7..91b3b24 100644 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DESCR("tsm_bernoulli_reset(internal)"); *** 5348,5353 **** --- 5348,5359 ---- DATA(insert OID = 3346 ( tsm_bernoulli_cost PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ tsm_bernoulli_cost _null_ _null_ _null_ )); DESCR("tsm_bernoulli_cost(internal)"); + /* rls */ + DATA(insert OID = 3298 ( row_security_active PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "25" _null_ _null_ _null_ _null_ _null_ row_security_active_name _null_ _null_ _null_ )); + DESCR("row security for current context active on table by table name"); + DATA(insert OID = 3299 ( row_security_active PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ _null_ row_security_active_id _null_ _null_ _null_ )); + DESCR("row security for current context active on table by table oid"); + /* * Symbolic values for provolatile column: these indicate whether the result * of a function is dependent *only* on the values of its explicit arguments, diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index fcb0bf0..5d80de3 100644 *** a/src/include/utils/builtins.h --- b/src/include/utils/builtins.h *************** *** 22,27 **** --- 22,28 ---- */ /* acl.c */ + extern Oid convert_table_name(text *tablename); extern Datum has_any_column_privilege_name_name(PG_FUNCTION_ARGS); extern Datum has_any_column_privilege_name_id(PG_FUNCTION_ARGS); extern Datum has_any_column_privilege_id_name(PG_FUNCTION_ARGS); *************** extern Datum set_config_by_name(PG_FUNCT *** 1119,1124 **** --- 1120,1129 ---- extern Datum show_all_settings(PG_FUNCTION_ARGS); extern Datum show_all_file_settings(PG_FUNCTION_ARGS); + /* rls.c */ + extern Datum row_security_active_name(PG_FUNCTION_ARGS); + extern Datum row_security_active_id(PG_FUNCTION_ARGS); + /* lockfuncs.c */ extern Datum pg_lock_status(PG_FUNCTION_ARGS); extern Datum pg_advisory_lock_int8(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index 414299a..b17c637 100644 *** a/src/test/regress/expected/rowsecurity.out --- b/src/test/regress/expected/rowsecurity.out *************** SELECT * FROM current_check; *** 2837,2846 **** COMMIT; -- -- Collation support -- BEGIN; ! SET row_security = force; CREATE TABLE coll_t (c) AS VALUES ('bar'::text); CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C")); ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY; --- 2837,2880 ---- COMMIT; -- + -- check pg_stats view filtering + -- + SET row_security TO ON; + SET SESSION AUTHORIZATION rls_regress_user0; + ANALYZE current_check; + -- Stats visible + SELECT row_security_active('current_check'); + row_security_active + --------------------- + f + (1 row) + + SELECT most_common_vals FROM pg_stats where tablename = 'current_check'; + most_common_vals + --------------------- + + + {rls_regress_user1} + (3 rows) + + SET SESSION AUTHORIZATION rls_regress_user1; + -- Stats not visible + SELECT row_security_active('current_check'); + row_security_active + --------------------- + t + (1 row) + + SELECT most_common_vals FROM pg_stats where tablename = 'current_check'; + most_common_vals + ------------------ + (0 rows) + + -- -- Collation support -- BEGIN; ! SET row_security TO FORCE; CREATE TABLE coll_t (c) AS VALUES ('bar'::text); CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C")); ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY; diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index cd53375..2fc05a5 100644 *** a/src/test/regress/expected/rules.out --- b/src/test/regress/expected/rules.out *************** pg_stats| SELECT n.nspname AS schemaname *** 2061,2067 **** JOIN pg_class c ON ((c.oid = s.starelid))) JOIN pg_attribute a ON (((c.oid = a.attrelid) AND (a.attnum = s.staattnum)))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) ! WHERE ((NOT a.attisdropped) AND has_column_privilege(c.oid, a.attnum, 'select'::text)); pg_tables| SELECT n.nspname AS schemaname, c.relname AS tablename, pg_get_userbyid(c.relowner) AS tableowner, --- 2061,2067 ---- JOIN pg_class c ON ((c.oid = s.starelid))) JOIN pg_attribute a ON (((c.oid = a.attrelid) AND (a.attnum = s.staattnum)))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) ! WHERE ((NOT a.attisdropped) AND has_column_privilege(c.oid, a.attnum, 'select'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid)))); pg_tables| SELECT n.nspname AS schemaname, c.relname AS tablename, pg_get_userbyid(c.relowner) AS tableowner, diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql index 039070b..7c377ca 100644 *** a/src/test/regress/sql/rowsecurity.sql --- b/src/test/regress/sql/rowsecurity.sql *************** SELECT * FROM current_check; *** 1137,1146 **** COMMIT; -- -- Collation support -- BEGIN; ! SET row_security = force; CREATE TABLE coll_t (c) AS VALUES ('bar'::text); CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C")); ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY; --- 1137,1161 ---- COMMIT; -- + -- check pg_stats view filtering + -- + SET row_security TO ON; + SET SESSION AUTHORIZATION rls_regress_user0; + ANALYZE current_check; + -- Stats visible + SELECT row_security_active('current_check'); + SELECT most_common_vals FROM pg_stats where tablename = 'current_check'; + + SET SESSION AUTHORIZATION rls_regress_user1; + -- Stats not visible + SELECT row_security_active('current_check'); + SELECT most_common_vals FROM pg_stats where tablename = 'current_check'; + + -- -- Collation support -- BEGIN; ! SET row_security TO FORCE; CREATE TABLE coll_t (c) AS VALUES ('bar'::text); CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C")); ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers