Re: Strange permission problem regarding pg_settings - Mailing list pgsql-general

From Joe Conway
Subject Re: Strange permission problem regarding pg_settings
Date
Msg-id 3FED2A66.80004@joeconway.com
Whole thread Raw
In response to Re: Strange permission problem regarding pg_settings  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: Strange permission problem regarding pg_settings
List pgsql-general
Tom Lane wrote:
> I suspect the fact that the pre-patch code made the "right" permissions
> check was really coincidental, and that the correct fix will not involve
> reversion of that patch but rather adding a facility somewhere to ensure
> that the original view gets properly permission-checked even if there's
> a DO INSTEAD NOTHING rule.  However, before biting that bullet it'd
> probably be good to understand in detail what's happening in both the
> 7.3.2 and CVS-tip code.  I have not looked at just why that patch
> changes this example's behavior.
>

I just started looking at this again. There is definitely an issue in
cvs tip:

create table t(f1 int, f2 text);
insert into t values(1, 'abc');
create view v as select * from t;
CREATE RULE v_upd AS ON UPDATE TO v DO INSTEAD
   UPDATE t SET f1 = NEW.f1, f2 = NEW.f2 WHERE f1 = OLD.f1;
create user user1;

-- this fails; as it should, I think
\c - user1
update v set f2 = 'def' where f1 = 1;
ERROR:  permission denied for relation v

-- so grant SELECT on the view
\c - postgres
grant select on v to public;

-- this should fail, but doesn't
\c - user1
update v set f2 = 'def' where f1 = 1;
UPDATE 1


On 7.3.2 that last section of the above script gives:

\c - user1
update v set f2 = 'def' where f1 = 1;
ERROR:  v: permission denied

The comment associated with the change says this:

  * Also, we must disable write-access checking in all the RT entries
  * copied from the main query.  This is safe since in fact the rule
  * action won't write on them, and it's necessary because the rule
  * action may have a different commandType than the main query, causing
  * ExecCheckRTEPerms() to make an inappropriate check.  The read-access
  * checks can be left enabled, although they're probably redundant.
  */

So SELECT permissions get checked for user1, but write-access does not.
The underlying table should be checked for permissions based on the rule
owner per rewriteDefine.c around line 439 (line 387 in 7.3.2):

  /*
   * We want the rule's table references to be checked as though by the
   * rule owner, not the user referencing the rule.  Therefore, scan
   * through the rule's rtables and set the checkAsUser field on all
   * rtable entries.
   */

Since the rule owner in this case is also the creator of the table, the
UPDATE suceeds.

ISTM that we want the relations in the un-rewritten query checked based
on the basis of the user referencing the rule and for the modes used in
the un-rewritten query -- suggesting the change need be reverted. Then
we want the rule's table references checked based on rule owner and
actual operations performed. It looks like this part should be what's
happening.

I went back to the original complaint -- here is the example on a 7.3.2
installation:

regression=# create table table1 (test1 integer);
grant insert on table1 to pleb;
create rule test_rule as on insert to table1 do update table2 set test2
= 2 where test2 = 0;
\c - pleb;
insert into table1 values (1);
CREATE TABLE
regression=# create table table2 (test2 integer);
CREATE TABLE
regression=# create user pleb;
ERROR:  CREATE USER: user name "pleb" already exists
regression=# grant insert on table1 to pleb;
GRANT
regression=# create rule test_rule as on insert to table1 do update
table2 set test2 = 2 where test2 = 0;
CREATE RULE
regression=# \c - pleb;
You are now connected as new user pleb.
regression=> insert into table1 values (1);
ERROR:  table1: permission denied

A few NOTICES placed in ExecCheckRTEPerms() reveals this:

regression=> insert into table1 values (1);
NOTICE:  relOid = 1245674
NOTICE:  userid = 101
NOTICE:  operation = CMD_INSERT
NOTICE:  relOid = 1245674
NOTICE:  userid = 101
NOTICE:  operation = CMD_UPDATE
ERROR:  table1: permission denied

regression=> select oid, relname from pg_class where relname like 'table%';
    oid   | relname
---------+---------
  1245674 | table1
  1245676 | table2
(2 rows)

It seems that second pass through ExecCheckRTEPerms() is not doing the
right thing. It ought to be checking table2 (not table1) for UPDATE as
userid == 1 (not 101), shouldn't it?

Any thoughts on where to look next?

Thanks,

Joe



pgsql-general by date:

Previous
From: "Ausrack Webmaster"
Date:
Subject: Re: dump 7.3 into 7.2?
Next
From: "Chris Travers"
Date:
Subject: Re: Is my MySQL Gaining ?