Commit 4f3b2a88 authored by Stephen Frost's avatar Stephen Frost

Enforce ALL/SELECT policies in RETURNING for RLS

For the UPDATE/DELETE RETURNING case, filter the records which are not
visible to the user through ALL or SELECT policies from those considered
for the UPDATE or DELETE.  This is similar to how the GRANT system
works, which prevents RETURNING unless the caller has SELECT rights on
the relation.

Per discussion with Robert, Dean, Tom, and Kevin.

Back-patch to 9.5 where RLS was introduced.
parent 22eaf35c
......@@ -186,6 +186,33 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
securityQuals,
hasSubLinks);
/*
* For the target relation, when there is a returning list, we need to
* collect up CMD_SELECT policies and add them via add_security_quals.
* This is because, for the RETURNING case, we have to filter any records
* which are not visible through an ALL or SELECT USING policy.
*
* We don't need to worry about the non-target relation case because we are
* checking the ALL and SELECT policies for those relations anyway (see
* above).
*/
if (root->returningList != NIL &&
(commandType == CMD_UPDATE || commandType == CMD_DELETE))
{
List *returning_permissive_policies;
List *returning_restrictive_policies;
get_policies_for_relation(rel, CMD_SELECT, user_id,
&returning_permissive_policies,
&returning_restrictive_policies);
add_security_quals(rt_index,
returning_permissive_policies,
returning_restrictive_policies,
securityQuals,
hasSubLinks);
}
/*
* For INSERT and UPDATE, add withCheckOptions to verify that any new
* records added are consistent with the security policies. This will use
......@@ -233,6 +260,26 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
withCheckOptions,
hasSubLinks);
/*
* Get and add ALL/SELECT policies, if there is a RETURNING clause,
* also as WCO policies, again, to avoid silently dropping data.
*/
if (root->returningList != NIL)
{
List *conflict_returning_permissive_policies = NIL;
List *conflict_returning_restrictive_policies = NIL;
get_policies_for_relation(rel, CMD_SELECT, user_id,
&conflict_returning_permissive_policies,
&conflict_returning_restrictive_policies);
add_with_check_options(rel, rt_index,
WCO_RLS_CONFLICT_CHECK,
conflict_returning_permissive_policies,
conflict_returning_restrictive_policies,
withCheckOptions,
hasSubLinks);
}
/* Enforce the WITH CHECK clauses of the UPDATE policies */
add_with_check_options(rel, rt_index,
WCO_RLS_UPDATE_CHECK,
......
......@@ -1246,21 +1246,23 @@ NOTICE: f_leak => cde
EXPLAIN (COSTS OFF) UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2
WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b
AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2;
QUERY PLAN
---------------------------------------------------------------
QUERY PLAN
---------------------------------------------------------------------
Update on t2 t2_1_1
-> Nested Loop
Join Filter: (t2_1.b = t2_2.b)
-> Subquery Scan on t2_1
Filter: f_leak(t2_1.b)
-> LockRows
-> Seq Scan on t2 t2_1_2
Filter: ((a = 3) AND ((a % 2) = 1))
-> Subquery Scan on t2_1_2
Filter: ((t2_1_2.a % 2) = 1)
-> LockRows
-> Seq Scan on t2 t2_1_3
Filter: ((a = 3) AND ((a % 2) = 1))
-> Subquery Scan on t2_2
Filter: f_leak(t2_2.b)
-> Seq Scan on t2 t2_2_1
Filter: ((a = 3) AND ((a % 2) = 1))
(12 rows)
(14 rows)
UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2
WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b
......@@ -1275,8 +1277,8 @@ NOTICE: f_leak => cde
EXPLAIN (COSTS OFF) UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2
WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b
AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
QUERY PLAN
---------------------------------------------------------------
QUERY PLAN
---------------------------------------------------------------------
Update on t1 t1_1_3
Update on t1 t1_1_3
Update on t2 t1_1
......@@ -1285,9 +1287,11 @@ AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
Join Filter: (t1_1.b = t1_2.b)
-> Subquery Scan on t1_1
Filter: f_leak(t1_1.b)
-> LockRows
-> Seq Scan on t1 t1_1_4
Filter: ((a = 4) AND ((a % 2) = 0))
-> Subquery Scan on t1_1_4
Filter: ((t1_1_4.a % 2) = 0)
-> LockRows
-> Seq Scan on t1 t1_1_5
Filter: ((a = 4) AND ((a % 2) = 0))
-> Subquery Scan on t1_2
Filter: f_leak(t1_2.b)
-> Append
......@@ -1301,9 +1305,11 @@ AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
Join Filter: (t1_1_1.b = t1_2_1.b)
-> Subquery Scan on t1_1_1
Filter: f_leak(t1_1_1.b)
-> LockRows
-> Seq Scan on t2 t1_1_5
Filter: ((a = 4) AND ((a % 2) = 0))
-> Subquery Scan on t1_1_6
Filter: ((t1_1_6.a % 2) = 0)
-> LockRows
-> Seq Scan on t2 t1_1_7
Filter: ((a = 4) AND ((a % 2) = 0))
-> Subquery Scan on t1_2_1
Filter: f_leak(t1_2_1.b)
-> Append
......@@ -1317,9 +1323,11 @@ AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
Join Filter: (t1_1_2.b = t1_2_2.b)
-> Subquery Scan on t1_1_2
Filter: f_leak(t1_1_2.b)
-> LockRows
-> Seq Scan on t3 t1_1_6
Filter: ((a = 4) AND ((a % 2) = 0))
-> Subquery Scan on t1_1_8
Filter: ((t1_1_8.a % 2) = 0)
-> LockRows
-> Seq Scan on t3 t1_1_9
Filter: ((a = 4) AND ((a % 2) = 0))
-> Subquery Scan on t1_2_2
Filter: f_leak(t1_2_2.b)
-> Append
......@@ -1329,7 +1337,7 @@ AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
Filter: ((a = 4) AND ((a % 2) = 0))
-> Seq Scan on t3 t1_2_11
Filter: ((a = 4) AND ((a % 2) = 0))
(52 rows)
(58 rows)
UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2
WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b
......@@ -1960,8 +1968,6 @@ NOTICE: f_leak => fgh_updt
(6 rows)
DELETE FROM x1 WHERE f_leak(b) RETURNING *;
NOTICE: f_leak => abc_updt
NOTICE: f_leak => efg_updt
NOTICE: f_leak => cde_updt
NOTICE: f_leak => fgh_updt
NOTICE: f_leak => bcd_updt_updt
......@@ -1970,15 +1976,13 @@ NOTICE: f_leak => fgh_updt_updt
NOTICE: f_leak => fgh_updt_updt
a | b | c
---+---------------+-------------------
1 | abc_updt | rls_regress_user1
5 | efg_updt | rls_regress_user1
3 | cde_updt | rls_regress_user2
7 | fgh_updt | rls_regress_user2
2 | bcd_updt_updt | rls_regress_user1
4 | def_updt_updt | rls_regress_user2
6 | fgh_updt_updt | rls_regress_user1
8 | fgh_updt_updt | rls_regress_user2
(8 rows)
(6 rows)
--
-- Duplicate Policy Names
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment