Hi

I installed a postgres-application (which was developed on debian 
woody) on red hat 9 today, using the postgres 7.3 rpms from redhad.
One of my the triggers uses the pg_settings table (more precisely, it 
updates that table to change the search_path temporarily). With the 
postgres 7.3 (and 7.4 too) installed on my debian development system, 
this worked fine. On redhat 9, however, I get an "pg_settings: 
permission denied" error when my trigger is executed.

The same thing happens when I try altering the pg_settings table from 
the commandline. (But of course works, when connected as superuser). I 
double-checked the permissions set on both the pg_settings view, and 
the set_config(text, text, bool)-function called from the update-rule 
for pg_settings, and both seem to be correct (and the same as on the 
debian machine).

As I needed to get the thing running, I now solved the problem by 
making the user that my app connects as a superuser, but I'd like to 
get rid of this again...

Are there any more permission I could check, or perhaps some 
config-option in postgres.conf that I could try?

greetings, Florian Pflug

PS: I also tried moving the postgres-data-dir away, and creating a 
fresh one with initdb - but with no success - still "pg_settings: 
permission denied"


Wed, 10 Dec 2003 08:05:28 +0100
"Florian G. Pflug" <fgp@phlo.org>
Strange permission problem regarding pg_settings
Florian G. Pflug wrote:
> I installed a postgres-application (which was developed on debian woody) 
> on red hat 9 today, using the postgres 7.3 rpms from redhad.
> One of my the triggers uses the pg_settings table (more precisely, it 
> updates that table to change the search_path temporarily). With the 
> postgres 7.3 (and 7.4 too) installed on my debian development system, 
> this worked fine. On redhat 9, however, I get an "pg_settings: 
> permission denied" error when my trigger is executed.

I've got Red Hat 9 here, but it is hard to guess what might be wrong 
without seeing some details. Can you post a self-contained example that 
recreates the problem?

Joe



Tue, 09 Dec 2003 23:19:39 -0800
Joe Conway <mail@joeconway.com>
Re: Strange permission problem regarding pg_settings

On Dec 10, 2003, at 8:19 AM, Joe Conway wrote:
> Florian G. Pflug wrote:
>> I installed a postgres-application (which was developed on debian 
>> woody) on red hat 9 today, using the postgres 7.3 rpms from redhad.
>> One of my the triggers uses the pg_settings table (more precisely, it 
>> updates that table to change the search_path temporarily). With the 
>> postgres 7.3 (and 7.4 too) installed on my debian development system, 
>> this worked fine. On redhat 9, however, I get an "pg_settings: 
>> permission denied" error when my trigger is executed.
> I've got Red Hat 9 here, but it is hard to guess what might be wrong 
> without seeing some details. Can you post a self-contained example 
> that recreates the problem?

This is what I did:
As user postgres (connected to template1)
.) create user testuser password 'pw' nocreatedb nocreateuser
.) create database testdb owner testuser encoding 'utf-8'

As user testuser (connected to testdb) :
.) update pg_settings set setting='public' where name='search_path' ;
this gives "pg_settings: permission denied"

.) select set_config('search_path', 'public', 'f') ;
this works, and sets the search_path as expected to 'public'

On debian(woody), with a woody-backport of postgresql-7.3 installed 
(the packages for sid recompiled for woody, and installed with dpkg), 
the "update pg_settings..." statement works.

As soon as I remove the "nocreateuser" from the "create user 
testuser...." line (or alter the user afterwards), it works on redhat 
too (but the user is superuser then, of course...)

If you need further information, or want me to test something, just say 
so ;-)

greetings, Florian Pflug


Wed, 10 Dec 2003 14:57:08 +0100
"Florian G. Pflug" <fgp@phlo.org>
Re: Strange permission problem regarding pg_settings
"Florian G. Pflug" <fgp@phlo.org> writes:
> As user testuser (connected to testdb) :
> .) update pg_settings set setting='public' where name='search_path' ;
> this gives "pg_settings: permission denied"

Hm.  Works fine here.  What do you get from

select relacl, relacl is null from pg_class where relname = 'pg_settings';


			regards, tom lane

Wed, 10 Dec 2003 11:35:38 -0500
Tom Lane <tgl@sss.pgh.pa.us>
Re: Strange permission problem regarding pg_settings 

On Dec 10, 2003, at 5:35 PM, Tom Lane wrote:
> "Florian G. Pflug" <fgp@phlo.org> writes:
>> As user testuser (connected to testdb) :
>> .) update pg_settings set setting='public' where name='search_path' ;
>> this gives "pg_settings: permission denied"
> Hm.  Works fine here.  What do you get from
> select relacl, relacl is null from pg_class where relname = 
> 'pg_settings';

testdb=> select relacl, relacl is null from pg_class where relname = 
'pg_settings' ;
  relacl | ?column?
--------+----------
  {=r}   | f
(1 row)

mfg, Florian Pflug


Wed, 10 Dec 2003 18:08:41 +0100
"Florian G. Pflug" <fgp@phlo.org>
Re: Strange permission problem regarding pg_settings 
Tom Lane wrote:
> "Florian G. Pflug" <fgp@phlo.org> writes:
>>As user testuser (connected to testdb) :
>>.) update pg_settings set setting='public' where name='search_path' ;
>>this gives "pg_settings: permission denied"
> 
> Hm.  Works fine here.  What do you get from
> 
> select relacl, relacl is null from pg_class where relname = 'pg_settings';

Works fine here too, on RH9:

Welcome to psql 7.3.5, the PostgreSQL interactive terminal.

Type:  \copyright for distribution terms
        \h for help with SQL commands
        \? for help on internal slash commands
        \g or terminate with semicolon to execute query
        \q to quit

regression=# \c template1
You are now connected to database template1.
template1=# create user testuser password 'pw' nocreatedb nocreateuser;
CREATE USER
template1=# create database testdb owner testuser encoding 'utf-8';
CREATE DATABASE
template1=# \c testdb testuser
You are now connected to database testdb as user testuser.
testdb=> update pg_settings set setting='public' where name='search_path' ;
  set_config
------------
  public
(1 row)

testdb=> select relacl, relacl is null from pg_class where relname = 
'pg_settings';
  relacl | ?column?
--------+----------
  {=r}   | f
(1 row)


Joe


Wed, 10 Dec 2003 09:27:27 -0800
Joe Conway <mail@joeconway.com>
Re: Strange permission problem regarding pg_settings
Joe Conway <mail@joeconway.com> writes:
> Works fine here too, on RH9:

> testdb=> update pg_settings set setting='public' where name='search_path' ;
>   set_config
> ------------
>   public
> (1 row)

> testdb=> select relacl, relacl is null from pg_class where relname = 
> 'pg_settings';
>   relacl | ?column?
> --------+----------
>   {=r}   | f
> (1 row)

Hm.  By rights it *should* fail, since the ACL is clearly not granting
UPDATE permissions to anybody.

The fact that it fails to fail seems to be because the rules on
pg_settings rewrite the UPDATE into DO INSTEAD NOTHING (which does
nothing, in particular makes no permission checks) and a SELECT,
which only requires read-permission on pg_settings.  This is probably
bogus and we ought to see what we can do about fixing it.  (And we'd
better fix initdb to grant UPDATE on pg_settings to public, too.)

Now, why does Florian see a permissions failure (which is really the
*right* behavior) when we don't?  He didn't say exactly which PG version
he was running, but I see a likely-related bug fix between 7.3.2 and
7.3.3:

2003-02-13 16:40  tgl

	* src/backend/rewrite/rewriteHandler.c (REL7_3_STABLE): Repair rule
	permissions-checking bug reported by Tim Burgess 10-Feb-02: the
	table(s) modified by the original query would get checked for the
	type of write permission needed by a rule query.

This fix may need to be rethought.  I'm not sure though where is a clean
place to plug in the UPDATE permissions check given that the rules for
this case do not generate any UPDATE query.

			regards, tom lane

Wed, 10 Dec 2003 14:32:03 -0500
Tom Lane <tgl@sss.pgh.pa.us>
Re: Strange permission problem regarding pg_settings 
Tom Lane wrote:
> Now, why does Florian see a permissions failure (which is really the
> *right* behavior) when we don't?  He didn't say exactly which PG version
> he was running, but I see a likely-related bug fix between 7.3.2 and
> 7.3.3:

That seems to be it:

# psql regression
Welcome to psql 7.3.2, the PostgreSQL interactive terminal.

Type:  \copyright for distribution terms
        \h for help with SQL commands
        \? for help on internal slash commands
        \g or terminate with semicolon to execute query
        \q to quit

regression=# \c template1
You are now connected to database template1.
template1=# create user testuser password 'pw' nocreatedb nocreateuser;
CREATE USER
template1=# create database testdb owner testuser encoding 'utf-8';
CREATE DATABASE
template1=# \c testdb testuser
You are now connected to database testdb as user testuser.
testdb=> update pg_settings set setting='public' where name='search_path' ;
ERROR:  pg_settings: permission denied

> This fix may need to be rethought.  I'm not sure though where is a clean
> place to plug in the UPDATE permissions check given that the rules for
> this case do not generate any UPDATE query.

Do you want me to take a look at this, or are you planning to?

Joe



Wed, 10 Dec 2003 13:39:13 -0800
Joe Conway <mail@joeconway.com>
Re: Strange permission problem regarding pg_settings
Joe Conway <mail@joeconway.com> writes:
> Tom Lane wrote:
>> This fix may need to be rethought.  I'm not sure though where is a clean
>> place to plug in the UPDATE permissions check given that the rules for
>> this case do not generate any UPDATE query.

> Do you want me to take a look at this, or are you planning to?

If you have any ideas, feel free to take a shot.  I've not thought of
anything I like.

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.

			regards, tom lane

Wed, 10 Dec 2003 17:01:48 -0500
Tom Lane <tgl@sss.pgh.pa.us>
Re: Strange permission problem regarding pg_settings 
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



Fri, 26 Dec 2003 22:44:54 -0800
Joe Conway <mail@joeconway.com>
Re: Strange permission problem regarding pg_settings
[ please respect moving of thread to pg-hackers ]

Joe Conway <mail@joeconway.com> writes:
> 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.

Reverting the change will bring back the bug for which it was created.
It does seem though that we have an inadequate model of how to perform
permission checks.  In particular, the "write" flag bit in RTEs is
context dependent: it can mean insert, update, or delete permission
depending on the surrounding command.

The problem the earlier bug report identified is really that when an RTE
is copied from one query to another, the meaning of its "write" flag bit
changes --- incorrectly --- if the new query is of a different type.
I thought when making that patch that we could make an end-run around
this problem by zeroing out the flag bit, but what we're now realizing
is that that leaves us with no check at all in some scenarios (because
the original query will be dropped completely when INSTEAD is specified).

I begin to think that the only real solution is to change the RTE
representation to identify the exact permission bits to be checked for
each entry (say, replace the read and write booleans with a permission
bitmask).  Then a view reference specifying INSERT permission check
could be copied into an UPDATE query without changing its permission
semantics.

This would be a fairly extensive change though.  Does anyone see an
easier way?

Also, does anyone see a case where it would be correct for the checked
permission to change when an RTE is copied to a query of a different
type?

			regards, tom lane

Sat, 27 Dec 2003 14:10:26 -0500
Tom Lane <tgl@sss.pgh.pa.us>
Re: Strange permission problem regarding pg_settings 
Tom Lane wrote:
> Reverting the change will bring back the bug for which it was created.
> It does seem though that we have an inadequate model of how to perform
> permission checks.  In particular, the "write" flag bit in RTEs is
> context dependent: it can mean insert, update, or delete permission
> depending on the surrounding command.

Sorry if I'm being thick, but what of 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)

Given how rules are supposed to work, the first check looks correct:
   INSERT on table1 checked as pleb, userid = 101

But the second check is incorrect, not because of the mode being 
checked, but because of the reloid and userid. The second check should be:
   UPDATE on table2 checked as postgres, userid = 1

So why doesn't the second rte refer to table2 and userid=1?

Joe



Sat, 27 Dec 2003 13:18:18 -0800
Joe Conway <mail@joeconway.com>
Re: [GENERAL] Strange permission problem regarding pg_settings
Joe Conway <mail@joeconway.com> writes:
> Sorry if I'm being thick, but what of this?

This is exactly what I'm talking about.  The rtable for a query
generated by a rule is the concatenation of the original query's rtable
and the rule query's rtable.  Therefore the RTE for table1 appears
twice, once in the original INSERT query and once in the generated
UPDATE query (even though the UPDATE query does not actually use that
RTE in this case).  This would be okay if the RTE's write permission
flag were not context-dependent, but because it is, we have a problem.

The patch I put into 7.3.3 assumed that we could just suppress
permission checks on the copied RTEs, but if the original query is
getting dropped due to INSTEAD, we really need to carry out those
permission checks in the generated query --- there is no place else.
So AFAICS we must make the permission checks non-context-dependent.

			regards, tom lane

Sat, 27 Dec 2003 17:14:51 -0500
Tom Lane <tgl@sss.pgh.pa.us>
Re: [GENERAL] Strange permission problem regarding pg_settings 
Tom Lane wrote:
> This is exactly what I'm talking about.  The rtable for a query
> generated by a rule is the concatenation of the original query's rtable
> and the rule query's rtable.  Therefore the RTE for table1 appears
> twice, once in the original INSERT query and once in the generated
> UPDATE query (even though the UPDATE query does not actually use that
> RTE in this case).  This would be okay if the RTE's write permission
> flag were not context-dependent, but because it is, we have a problem.

OK, that makes more sense now. But why isn't table2 also in the rule 
query's rtable?

Joe


Sat, 27 Dec 2003 14:51:47 -0800
Joe Conway <mail@joeconway.com>
Re: [GENERAL] Strange permission problem regarding pg_settings
Joe Conway <mail@joeconway.com> writes:
> OK, that makes more sense now. But why isn't table2 also in the rule 
> query's rtable?

It is, but you errored out before getting to it.

			regards, tom lane

Sat, 27 Dec 2003 18:16:54 -0500
Tom Lane <tgl@sss.pgh.pa.us>
Re: [GENERAL] Strange permission problem regarding pg_settings 
Tom Lane wrote:
> Joe Conway <mail@joeconway.com> writes:
>>OK, that makes more sense now. But why isn't table2 also in the rule 
>>query's rtable?
> 
> It is, but you errored out before getting to it.

The fog has finally started lifting, I think.

Why wouldn't we force checkAsUser to the rule owner in the copied RTEs, 
similar to the rest of the rule query? It makes sense in that the rule 
query could possibly use the RTE (although as you pointed out it doesn't 
in this case), and therefore the permission check should be the same, no?

Joe



Sat, 27 Dec 2003 21:06:19 -0800
Joe Conway <mail@joeconway.com>
Re: [GENERAL] Strange permission problem regarding pg_settings
Joe Conway <mail@joeconway.com> writes:
> Why wouldn't we force checkAsUser to the rule owner in the copied RTEs, 
> similar to the rest of the rule query?

Because it would be the wrong check.  We need to check that the rule
caller has permissions on the view for whatever he originally tried
to do (ie, the type of the original query that referenced the view).
In the non-INSTEAD case, this check will be redundant with a check
applied when the original query is executed ... but in the INSTEAD case,
it isn't redundant.

> It makes sense in that the rule 
> query could possibly use the RTE (although as you pointed out it doesn't 
> in this case), and therefore the permission check should be the same, no?

No; it's possible for the amalgamated query to include references to
tables that are referenced only in the original query and nowhere in the
text of the rule.  (This is obviously possible right now, since we just
take the union of the two rtables and don't make any effort to discard
unreferenced RTEs ... but I think it could happen even if we did discard
unreferenced RTEs, because conditions from the original query get pushed
into the rule and might reference tables that the rule text doesn't
mention.)  Checking such tables for rule-owner access would be wrong;
they have to be checked for access by the rule caller.

			regards, tom lane

Sun, 28 Dec 2003 11:11:20 -0500
Tom Lane <tgl@sss.pgh.pa.us>
Re: [GENERAL] Strange permission problem regarding pg_settings 
Tom Lane wrote:
> No; it's possible for the amalgamated query to include references to
> tables that are referenced only in the original query and nowhere in the
> text of the rule.  (This is obviously possible right now, since we just
> take the union of the two rtables and don't make any effort to discard
> unreferenced RTEs ... but I think it could happen even if we did discard
> unreferenced RTEs, because conditions from the original query get pushed
> into the rule and might reference tables that the rule text doesn't
> mention.)  Checking such tables for rule-owner access would be wrong;
> they have to be checked for access by the rule caller.

OK, so the permission check performed on the original query RTEs, while 
executing the rule query is:

  1) redundant for non-INSTEAD cases
and
  2) wrong if the original query and rule query are different modes

The patch at the root of this discussion eliminates both issues, but 
leaves us with no check at all in the INSTEAD case. Is there any way to 
do the permission checks on the original query in the INSTEAD case, even 
though the query itself will never be executed?

Joe



Sun, 28 Dec 2003 12:11:59 -0800
Joe Conway <mail@joeconway.com>
Re: [GENERAL] Strange permission problem regarding pg_settings
Joe Conway <mail@joeconway.com> writes:
> The patch at the root of this discussion eliminates both issues, but 
> leaves us with no check at all in the INSTEAD case. Is there any way to 
> do the permission checks on the original query in the INSTEAD case, even 
> though the query itself will never be executed?

I thought about that a bit, but I'm not sure it's any less invasive a
change.  Currently the original query doesn't get shipped to the
executor at all, if it's suppressed by INSTEAD.  In place of the
original query, the rewriter would have to generate some kind of
"no-op query" that the executor would run as far as checking the rtable
permissions for, and then stop.  (There is a CMD_NOTHING value of
CmdType, but I don't think it's supported anywhere outside the rule
rewriter, and the rewriter's semantics for it may not be quite right
anyway.)  Adding a new CmdType would have cascading effects in a bunch
of places.  While it's probably not hard to fix any one of them
individually, overall this doesn't look any easier or cleaner than
extending the RTE permissions flag representation.

The only argument I can see in favor of doing it that way would be that
by avoiding a change in RTE representation, we would have a patch that
could be applied in the 7.4.* series.  (If we change RTEs then we can
only fix it in 7.5, because the change to stored rule representation
would force initdb.)  But the patch would be extensive enough that I'd
be pretty hesitant to apply it to 7.4.* anyway.

What might be the best compromise is to fix it properly in 7.5 and
revert the previous patch in 7.4.*.  That would restore correct checking
of view permissions for all but the case where a rule substitutes a
different query type; which is a bug that we had for a very long time
before anyone noticed, so maybe leaving it unfixed in 7.4.* is okay.

			regards, tom lane

Sun, 28 Dec 2003 15:41:59 -0500
Tom Lane <tgl@sss.pgh.pa.us>
Re: [GENERAL] Strange permission problem regarding pg_settings 