Re: WIP patch (v2) for updatable security barrier views - Mailing list pgsql-hackers

From Craig Ringer
Subject Re: WIP patch (v2) for updatable security barrier views
Date
Msg-id 52CD1438.9070400@2ndquadrant.com
Whole thread Raw
In response to Re: WIP patch (v2) for updatable security barrier views  (Dean Rasheed <dean.a.rasheed@gmail.com>)
Responses Re: WIP patch (v2) for updatable security barrier views
List pgsql-hackers
Dean,

Short version
-------------

Looks amazing overall. Very clever to zip up the s.b. quals, let the
rest of the rewriter and planer do their work normally, then unpack them
into subqueries inserted in the planner once inheritance appendrels are
expanded, etc.

My main concern is that the securityQuals appear to bypass all later
rewrite stages, inheritance expansion during planning, etc. I suspect
this might be hard to get around (because these are disembodied quals
which may have nonsense varnos), but I'm looking into it now.

There's also an assertion failure whenever a correlated subquery appears
as a security barrier view qual.  Again, looking at it.

Ideas on that issue?



Much longer version: My understanding of how it works
-----------------------------------------------------

My understanding from reading the patch is that this:

- Flattens target views in rewriteTargetView, as in current master. If
the target view is a security barrier view, the view quals are appended
to a list of security barrier quals on the new RTE, instead of appended
to the RTE's normal quals like for normal views.

After rewrite the views are fully flattened down to a RTE_RELATION,
which becomes the resultRelation. An unreferenced RTE for each view
that's been rewritten is preserved in the range-table for permissions
checking purposes only (same as current master).

- Inheritance expansion, tlist expansion, etc then occurrs as normal.

- In planning, in inheritance_planner, if any RTE has any stashed
security quals in its RangeTableEntry, expand_security_qual is invoked.
This iteratively wraps the base relation in a subquery with the saved
security barrier quals, creating nested subqueries around the original
RTE. At each pass resultRelation is changed to point to the new
outer-most subquery.


As a result of this approach everything looks normal to
preprocess_targetlist, row-marking, etc, because they're seeing a normal
RTE_RELATION as resultRelation. The security barrier quals are, at this
stage, stashed aside. If there's inheritance involved, RTEs copied
during appendrel expansion get copies of the security quals on in the
parent RTE.

Problem with inheritance, views, etc in s.b. quals
--------------------------------------------------

After inheritance expansion, tlist expansion, etc, the s.b. quals are
unpacked to create subqueries wrapping the original RTEs.


So, with:

CREATE TABLE t1 (x float, b integer, secret1 text, secret2 text);
CREATE TABLE t1child (z integer) INHERITS (t1);

INSERT INTO t1 (x, b, secret1, secret2)
VALUES
(0,0,'secret0', 'supersecret'),
(1,1,'secret1', 'supersecret'),
(2,2,'secret2', 'supersecret'),
(3,3,'secret3', 'supersecret'),
(4,4,'secret4', 'supersecret'),
(5,6,'secret5', 'supersecret');

INSERT INTO t1child (x, b, secret1, secret2, z)
VALUES
(8,8,'secret8', 'ss', 8),
(9,9,'secret8', 'ss', 9),
(10,10,'secret8', 'ss', 10);

CREATE VIEW v1
WITH (security_barrier)
AS
SELECT b AS b1, x AS x1, secret1
FROM t1 WHERE b % 2 = 0;

CREATE VIEW v2
WITH (security_barrier)
AS
SELECT b1 AS b2, x1 AS x2
FROM v1 WHERE b1 % 4 = 0;



then a statement like:

UPDATE v2
SET x2 = x2 + 32;

will be rewritten into something like (imaginary sql)

UPDATE t1 WITH SECURITY QUALS ((b % 2 == 0), (b % 4 == 0))
SET x = x + 32

inheritance-expanded and tlist-expanded into something like (imaginary SQL)


UPDATE(t1 WITH SECURITY QUALS ((b % 2 == 0), (b % 4 == 0)))UNION ALL(t1child WITH SECURITY QUALS ((b % 2 == 0), (b % 4
==0)))
 
SET x = x + 32;


after which security qual expansion occurs, giving us something like:


UPDATEt1, t1child <--- resultRelations(   SELECT v2.ctid, v2.*   FROM (     SELECT v1.ctid, v1.*     FROM (
SELECTt1.ctid, t1.*       FROM t1       WHERE b % 2 == 0     ) v1     WHERE b % 4 == 0   ) v2
 
   UNION ALL
   SELECT v2.ctid, v2.*   FROM (     SELECT v1.ctid, v1.*     FROM (       SELECT t1child.ctid, t1child.*       FROM
t1child      WHERE b % 2 == 0     ) v1     WHERE b % 4 == 0   ) v2
 
)
SET x = x + 32;


Giving a plan looking like:

EXPLAIN UPDATE v2 SET x2 = 32

                               QUERY PLAN
---------------------------------------------------------------------------Update on t1 t1_2  (cost=0.00..23.35 rows=2
width=76) ->  Subquery Scan on t1  (cost=0.00..2.18 rows=1 width=74)        ->  Subquery Scan on t1_3  (cost=0.00..2.17
rows=1width=74)              Filter: ((t1_3.b % 4) = 0)              ->  Seq Scan on t1 t1_4  (cost=0.00..2.16 rows=1
width=74)                   Filter: ((b % 2) = 0)  ->  Subquery Scan on t1_1  (cost=0.00..21.17 rows=1 width=78)
-> Subquery Scan on t1_5  (cost=0.00..21.16 rows=1 width=78)              Filter: ((t1_5.b % 4) = 0)              ->
SeqScan on t1child  (cost=0.00..21.10 rows=4 width=78)                    Filter: ((b % 2) = 0)
 
(11 rows)




So far this looks like a really clever approach. My only real concern is
that the security quals are currently hidden from rewrite and parsing
before during the period they're hidden away in the security quals RTI
field.

This means they aren't processed for things like inheritance expansion. e.g.

CREATE TABLE rowfilter (remainder integer, userid text);
CREATE TABLE rowfilterchild () INHERITS (rowfilter);
INSERT INTO rowfilterchild(remainder, userid) values (0, current_user);

a view with a security qual that refers to an inherited relation won't work:

CREATE VIEW sqv
WITH (security_barrier)
AS
SELECT x, b FROM t1 WHERE ( SELECT b % 4 = remainder FROM rowfilter WHERE userid = current_user OFFSET 0
);

This is a bit contrived to force the optimiser to treat the subquery as
correlated and thus make sure the ref to rowfilter gets into the
securityQuals list.

I expected zero results (a scan of rowfilter, but not rowfilterchild,
resulting in a null subquery return) but land up with an assertion
failure instead. The assertion triggers for any security qual containing
a correlated subquery, so it's crashing out before we can break due to
failure to expand inheritance.


This isn't just about inheritance. In general, we'd need a way to
process those securityQuals through any rewrite phases and through the
parts of planning before they get merged back in, so they're subject to
things like inheritance appendrel expansion.

Same if the security qual contains a view ref:

CREATE VIEW dumbview(zero)
AS SELECT 0;

CREATE VIEW sqv2
WITH (security_barrier)
AS
SELECT x, b FROM t1
WHERE (SELECT b % 2 = zero FROM dumbview OFFSET 0);


--
Craig Ringer
(Phew!)



pgsql-hackers by date:

Previous
From: Simon Riggs
Date:
Subject: Turning off HOT/Cleanup sometimes
Next
From: Heikki Linnakangas
Date:
Subject: Re: Standalone synchronous master