Enforce ALL/SELECT policies in RETURNING for RLS
authorStephen Frost <[email protected]>
Tue, 15 Sep 2015 19:49:31 +0000 (15:49 -0400)
committerPavan Deolasee <[email protected]>
Wed, 26 Oct 2016 07:45:49 +0000 (13:15 +0530)
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.

src/backend/rewrite/rowsecurity.c
src/test/regress/expected/rowsecurity.out

index 69eb9bdb2ae426d3222162bcf380d775f3805f14..c3d4b7ec1455a179edcd4e9c567e6f7c1eac2775 100644 (file)
@@ -188,6 +188,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
@@ -235,6 +262,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,
index 576bcafa49693caaa95c12383d48c6dcab93eb65..de6b61878993b3dead00e2fd6a9ea9604f613600 100644 (file)
@@ -1954,9 +1954,6 @@ DELETE FROM x1 WHERE f_leak(b) RETURNING *;
  2 | bcd_updt_updt | rls_regress_user1
  6 | fgh_updt_updt | rls_regress_user1
  8 | fgh_updt_updt | rls_regress_user2
- 3 | cde_updt      | rls_regress_user2
- 7 | fgh_updt      | rls_regress_user2
- 4 | def_updt_updt | rls_regress_user2
 (6 rows)
 
 --