Skip to content

Commit 1aebfbe

Browse files
committed
Fix security checks for selectivity estimation functions with RLS.
In commit e2d4ef8, security checks were added to prevent user-supplied operators from running over data from pg_statistic unless the user has table or column privileges on the table, or the operator is leakproof. For a table with RLS, however, checking for table or column privileges is insufficient, since that does not guarantee that the user has permission to view all of the column's data. Fix this by also checking for securityQuals on the RTE, and insisting that the operator be leakproof if there are any. Thus the leakproofness check will only be skipped if there are no securityQuals and the user has table or column privileges on the table -- i.e., only if we know that the user has access to all the data in the column. Back-patch to 9.5 where RLS was added. Dean Rasheed, reviewed by Jonathan Katz and Stephen Frost. Security: CVE-2019-10130
1 parent bd5e8b6 commit 1aebfbe

File tree

3 files changed

+56
-6
lines changed

3 files changed

+56
-6
lines changed

src/backend/utils/adt/selfuncs.c

+15-6
Original file line numberDiff line numberDiff line change
@@ -4597,9 +4597,13 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
45974597
* For simplicity, we insist on the whole
45984598
* table being selectable, rather than trying
45994599
* to identify which column(s) the index
4600-
* depends on.
4600+
* depends on. Also require all rows to be
4601+
* selectable --- there must be no
4602+
* securityQuals from security barrier views
4603+
* or RLS policies.
46014604
*/
46024605
vardata->acl_ok =
4606+
rte->securityQuals == NIL &&
46034607
(pg_class_aclcheck(rte->relid, GetUserId(),
46044608
ACL_SELECT) == ACLCHECK_OK);
46054609
}
@@ -4663,12 +4667,17 @@ examine_simple_variable(PlannerInfo *root, Var *var,
46634667

46644668
if (HeapTupleIsValid(vardata->statsTuple))
46654669
{
4666-
/* check if user has permission to read this column */
4670+
/*
4671+
* Check if user has permission to read this column. We require
4672+
* all rows to be accessible, so there must be no securityQuals
4673+
* from security barrier views or RLS policies.
4674+
*/
46674675
vardata->acl_ok =
4668-
(pg_class_aclcheck(rte->relid, GetUserId(),
4669-
ACL_SELECT) == ACLCHECK_OK) ||
4670-
(pg_attribute_aclcheck(rte->relid, var->varattno, GetUserId(),
4671-
ACL_SELECT) == ACLCHECK_OK);
4676+
rte->securityQuals == NIL &&
4677+
((pg_class_aclcheck(rte->relid, GetUserId(),
4678+
ACL_SELECT) == ACLCHECK_OK) ||
4679+
(pg_attribute_aclcheck(rte->relid, var->varattno, GetUserId(),
4680+
ACL_SELECT) == ACLCHECK_OK));
46724681
}
46734682
else
46744683
{

src/test/regress/expected/rowsecurity.out

+21
Original file line numberDiff line numberDiff line change
@@ -3937,6 +3937,27 @@ RESET SESSION AUTHORIZATION;
39373937
DROP VIEW rls_view;
39383938
DROP TABLE rls_tbl;
39393939
DROP TABLE ref_tbl;
3940+
-- Leaky operator test
3941+
CREATE TABLE rls_tbl (a int);
3942+
INSERT INTO rls_tbl SELECT x/10 FROM generate_series(1, 100) x;
3943+
ANALYZE rls_tbl;
3944+
ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
3945+
GRANT SELECT ON rls_tbl TO regress_rls_alice;
3946+
SET SESSION AUTHORIZATION regress_rls_alice;
3947+
CREATE FUNCTION op_leak(int, int) RETURNS bool
3948+
AS 'BEGIN RAISE NOTICE ''op_leak => %, %'', $1, $2; RETURN $1 < $2; END'
3949+
LANGUAGE plpgsql;
3950+
CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int,
3951+
restrict = scalarltsel);
3952+
SELECT * FROM rls_tbl WHERE a <<< 1000;
3953+
a
3954+
---
3955+
(0 rows)
3956+
3957+
DROP OPERATOR <<< (int, int);
3958+
DROP FUNCTION op_leak(int, int);
3959+
RESET SESSION AUTHORIZATION;
3960+
DROP TABLE rls_tbl;
39403961
--
39413962
-- Clean up objects
39423963
--

src/test/regress/sql/rowsecurity.sql

+20
Original file line numberDiff line numberDiff line change
@@ -1790,6 +1790,26 @@ DROP VIEW rls_view;
17901790
DROP TABLE rls_tbl;
17911791
DROP TABLE ref_tbl;
17921792

1793+
-- Leaky operator test
1794+
CREATE TABLE rls_tbl (a int);
1795+
INSERT INTO rls_tbl SELECT x/10 FROM generate_series(1, 100) x;
1796+
ANALYZE rls_tbl;
1797+
1798+
ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
1799+
GRANT SELECT ON rls_tbl TO regress_rls_alice;
1800+
1801+
SET SESSION AUTHORIZATION regress_rls_alice;
1802+
CREATE FUNCTION op_leak(int, int) RETURNS bool
1803+
AS 'BEGIN RAISE NOTICE ''op_leak => %, %'', $1, $2; RETURN $1 < $2; END'
1804+
LANGUAGE plpgsql;
1805+
CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int,
1806+
restrict = scalarltsel);
1807+
SELECT * FROM rls_tbl WHERE a <<< 1000;
1808+
DROP OPERATOR <<< (int, int);
1809+
DROP FUNCTION op_leak(int, int);
1810+
RESET SESSION AUTHORIZATION;
1811+
DROP TABLE rls_tbl;
1812+
17931813
--
17941814
-- Clean up objects
17951815
--

0 commit comments

Comments
 (0)