Deepak Bhole of Red Hat asked me about the following situation:

regression=# create table test (f1 int);
CREATE TABLE
regression=# revoke insert,update,delete,references on test from postgres;
REVOKE
regression=# \z test
      Access privileges for database "regression"
 Schema | Name | Type  |       Access privileges
--------+------+-------+--------------------------------
 public | test | table | {postgres=*r***R**t*/postgres}
(1 row)

It seems unreasonably hard to interpret what those stars mean, don't you
think?  Certainly you can't tell which star is which without hardwired
knowledge about the order in which the bits will be printed.

The problem here is that we allow the owner to revoke his own ordinary
privileges but not his grant options; so we end up with a permissions
configuration that was not considered in the design of the external
representation for ACL lists.  (Per spec it is not possible to hold a
grant option for a privilege without holding the privilege itself too,
and I expect that this printout format was designed assuming that
restriction.)

I think the printout format is fine and the silent non-removal of grant
options was a bad idea, particularly since it doesn't seem to be saving
any code (GRANT/REVOKE check ownerness anyway).  I propose that we take
out the special cases in merge_acl_with_grant that prohibit revoking an
owner's grant options, and instead adjust the grant statement code to
act as if those options are always present.  Instead of the existing

        if (stmt->is_grant
            && !pg_class_ownercheck(relOid, GetUserId())
            && pg_class_aclcheck(relOid, GetUserId(),
                                 ACL_GRANT_OPTION_FOR(privileges)) != ACLCHECK_OK)
            aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, relvar->relname);

it'd be something like

        if (pg_class_ownercheck(relOid, GetUserId())
	    { okay, assume we have all grant options }
	else if (pg_class_aclcheck(relOid, GetUserId(), ...) != ACLCHECK_OK)
	    { error }
	else
	    { determine actual grant options for non-owner }

Thus the effective behavior of grant/revoke would remain the same as
before, but we wouldn't have the contrary-to-spec cases in the visible
contents of the ACL list.

I am in the middle of fixing GRANT/REVOKE to conform to spec as
discussed in the bug #1150 thread, and will make this change too
if I don't hear any objections.

			regards, tom lane

Thu, 13 May 2004 17:01:50 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Bogus permissions display in 7.4
Tom Lane wrote:
> I think the printout format is fine and the silent non-removal of
> grant options was a bad idea, particularly since it doesn't seem to
> be saving any code (GRANT/REVOKE check ownerness anyway).  I propose
> that we take out the special cases in merge_acl_with_grant that
> prohibit revoking an owner's grant options, and instead adjust the
> grant statement code to act as if those options are always present. 

Sounds good.


Fri, 14 May 2004 00:37:24 +0200
Peter Eisentraut <peter_e@gmx.net>
Re: Bogus permissions display in 7.4
Peter Eisentraut <peter_e@gmx.net> writes:
> Tom Lane wrote:
>> I think the printout format is fine and the silent non-removal of
>> grant options was a bad idea, particularly since it doesn't seem to
>> be saving any code (GRANT/REVOKE check ownerness anyway).  I propose
>> that we take out the special cases in merge_acl_with_grant that
>> prohibit revoking an owner's grant options, and instead adjust the
>> grant statement code to act as if those options are always present. 

> Sounds good.

If you like that, I have a further suggestion, which is to not include
the owner's grant options in the default ACL, either.  This would not
affect the behavior given the above changes; what it would do is reduce
clutter in the ACL display.  Right now, if user miriam does

create table mytable(f int);
grant select on mytable to public;
\z mytable

she'll see

 Schema |  Name   | Type  |            Access privileges
--------+---------+-------+------------------------------------------
 public | mytable | table | {miriam=a*r*w*d*R*x*t*/miriam,=r/miriam}

Changing the default ACL would take this down to

 public | mytable | table | {miriam=arwdRxt/miriam,=r/miriam}

which seems usefully more readable to me.  Comments?

			regards, tom lane

Thu, 13 May 2004 19:43:45 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: Bogus permissions display in 7.4 
>  Schema |  Name   | Type  |            Access privileges
> --------+---------+-------+------------------------------------------
>  public | mytable | table | {miriam=a*r*w*d*R*x*t*/miriam,=r/miriam}
> 
> Changing the default ACL would take this down to
> 
>  public | mytable | table | {miriam=arwdRxt/miriam,=r/miriam}
> 
> which seems usefully more readable to me.  Comments?

Guess this means I have to tweak my ACL parser in phpPgAdmin.  If you 
could do something to make that less of a NIGHTMARE, i'd be all ears :P

ie.

1. Make it easy to convert an array to a rowset

2. Fabien's accessor functions? Would they help?

3. At least the quoting has been fixed in 7.4 though

4. Maybe even a function that takes an aclitem and returns....something...

Fri, 14 May 2004 09:54:41 +0800
Christopher Kings-Lynne <chriskl@familyhealth.com.au>
Re: Bogus permissions display in 7.4
I wrote:
> If you like that, I have a further suggestion, which is to not include
> the owner's grant options in the default ACL, either.

I've been thinking more about this, and realizing that there are more
implications than I first thought.  Specifically, we have to consider
how any hacking we do here will affect recursive_revoke().  The
different options I've suggested would have different side effects,
and I'm having a hard time deciding which is better.

In the existing 7.4 code, you can revoke the owner's privileges but not
his grant options.  This confuses the ACL display code (and possibly
clients that try to interpret ACL displays), and it means that you can't
use recursive_revoke to get rid of everyone but the owner's privileges.

In my proposal of earlier today, you can revoke the owner's grant
options, which will force recursive revocation of everyone else's
privileges (since these all flow ultimately from the owner's grants).
This implies that it is not possible for the owner to have less
privileges than anyone else.  Perhaps that is not bad, but up to now it
was possible to configure a table that way.  Another problem is that
because GRANT still acts as though the owner has grant options, he can
then go and re-grant privs to other people (or the superuser can do it).
Now you have an ACL in which privileges appear to flow from the owner
despite having no grant options.  That will confuse matters --- for
example, if the owner does REVOKE GRANT OPTION FROM himself a second
time, this time it will *not* recursively kill everyone else's
privileges, because recursive_revoke will not see any need to recurse.

If we remove the owner's grant options from the default ACL then
revoking the owner's grant options won't ever recurse (unless he first
grants them to himself explicitly and then revokes them).  Perhaps
that's good?  I'm not sure.

We could patch around some of these problems if recursive_revoke knew
who the owner was (and could thereby take into account the implicit
owner grant options that I still think we want to have).  But it does
not know that, and some of its callers do not either.

Messier and messier.  I'm beginning to see why the SQL spec wants to
introduce a _SYSTEM authid to be the original source of rights.

It could be that the only good solution is to introduce knowledge of the
owner directly into the ACL representation.  You could see the spec's
approach as doing that: he who gets his rights directly from _SYSTEM
is the owner.  Another perhaps more compact way is to make a separate
ACL_IDTYPE to represent "owner" (we are using only 3 of the 4 possible
bitpatterns so this would be easy).

Comments?

			regards, tom lane

Thu, 13 May 2004 22:03:21 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: Bogus permissions display in 7.4 

> Guess this means I have to tweak my ACL parser in phpPgAdmin.  If you
> could do something to make that less of a NIGHTMARE, i'd be all ears :P
>
> 1. Make it easy to convert an array to a rowset
> 2. Fabien's accessor functions? Would they help?

Sure. I needed these functions to convert acl array into relations
with some plpgsql. Basically it looks like your first item.
I'm happy not to be alone in the world trying to manipulate
acl from user land...

> 3. At least the quoting has been fixed in 7.4 though
> 4. Maybe even a function that takes an aclitem and returns....something...

This would be an accessor;-)

-- 
Fabien Coelho - coelho@cri.ensmp.fr

Fri, 14 May 2004 15:26:15 +0200 (CEST)
Fabien COELHO <coelho@cri.ensmp.fr>
Re: Bogus permissions display in 7.4
I wrote:
> I've been thinking more about this, and realizing that there are more
> implications than I first thought.  Specifically, we have to consider
> how any hacking we do here will affect recursive_revoke().

After further consideration I think that at least part of the problem
here is that recursive_revoke is wrong.  Or you could say that our ACL
datastructure is inadequate.  Consider the following situation:

1. A, the owner of some object, grants rights with grant option to
   both B and C.

2. B and C independently re-grant those rights with grant option to D.

3. D grants the rights to E.

4. A revokes the rights from B.

recursive_revoke will revoke B's grant to D, while not touching C's
grant to D, which is correct.  However it will then recurse to revoke
D's grant to E.  I claim this is wrong because D still has the rights
via C.

You could imagine extending the ACL datastructure to keep track of where
a grantor's rights came from, but this would be horribly messy, and
I'm not sure it's what we want anyway.  (For instance, we'd really need
to extend GRANT to allow D to say whether he was granting to E on the
basis of B's grant or C's grant.  Yech.)

I think the correct solution involves having recursive_revoke look to
see if D still has the rights from somewhere else before it goes off to
recursively revoke D's grants.  This makes it correctly implement the
notion that one always has the union of the rights granted by anyone.

Now having said that, does it improve the situations I was worried about
last night?  It does, but we really need to fix things so that the ACL
representation explicitly shows the owner.  Here's what I suggest:

1. The first entry of an ACL list should always be a grant of all
relevant grant options for the object type from "the system" (AclId 0,
let's say) to the object owner.  This entry represents the semantics
we've agreed to that an owner has nonrevocable grant options.

2. In a default ACL list there will also be an entry that grants all
regular privileges (not grant options) to the owner, from the owner.
This can be considered to be authorized by the system-granted grant
options.  Because it's set up this way, the owner can revoke his own
regular privileges --- make a table read-only, for instance.

3. It will not matter whether the owner grants himself grant options
(or revokes them later).  They'll be redundant with the system grant
and therefore have no effect on behavior.  Note we need the aforesaid
fix to recursive_revoke to make that true.  Also note that the system
grant *must not* grant regular privileges, or the owner couldn't make
his table read-only to himself.

4. I think that the system ACL entry should be "hidden" and not
displayed by ACL-list printing.  I'm not quite sure yet how to make
that happen.  It would be nicer if the owner ID could be passed to
recursive_revoke out-of-band, instead of being represented inside the
ACL list, but I don't see how to do that for all its callers.

Thoughts?

			regards, tom lane

Fri, 14 May 2004 10:35:22 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: Bogus permissions display in 7.4 
Basically our whole API of communicating ACL information to the user is
poor.  Look at psql \z:

	test=> create table test(x int);
	CREATE TABLE
	test=> grant all on test to public;
	GRANT
	test=> \z test
	                    Access privileges for database "test"
	 Schema | Name | Type  |                  Access privileges
	--------+------+-------+------------------------------------------------------
	
	 public | test | table | {postgres=a*r*w*d*R*x*t*/postgres,=arwdRxt/postgres}
	(1 row)

Do we actually expect folks to understand that?  What use is \z really
then?  Sure, maybe we document it in the GRANT manual page, but that
hardly makes it readable on its own.

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Fri, 14 May 2004 12:26:32 -0400 (EDT)
Bruce Momjian <pgman@candle.pha.pa.us>
Re: Bogus permissions display in 7.4
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> Basically our whole API of communicating ACL information to the user is
> poor.  Look at psql \z:

Well, if you don't like that, there's always the spec's way:

regression=> select * from information_schema.table_privileges where table_name = 'mytable';
 grantor | grantee | table_catalog | table_schema | table_name | privilege_type | is_grantable | with_hierarchy
---------+---------+---------------+--------------+------------+----------------+--------------+----------------
 foo     | foo     | regression    | public       | mytable    | SELECT         | YES          | NO
 foo     | foo     | regression    | public       | mytable    | DELETE         | YES          | NO
 foo     | foo     | regression    | public       | mytable    | INSERT         | YES          | NO
 foo     | foo     | regression    | public       | mytable    | UPDATE         | YES          | NO
 foo     | foo     | regression    | public       | mytable    | REFERENCES     | YES          | NO
 foo     | foo     | regression    | public       | mytable    | RULE           | YES          | NO
 foo     | foo     | regression    | public       | mytable    | TRIGGER        | YES          | NO
 foo     | bar     | regression    | public       | mytable    | SELECT         | NO           | NO
(8 rows)
 
I'm not sure this is really all that much more readable than

regression=> \z mytable
          Access privileges for database "regression"
 Schema |  Name   | Type  |         Access privileges
--------+---------+-------+------------------------------------
 public | mytable | table | {foo=a*r*w*d*R*x*t*/foo,bar=r/foo}
(1 row)
 
but it's there if you want it.

			regards, tom lane

Fri, 14 May 2004 12:41:16 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: Bogus permissions display in 7.4 

Dear Tom,

> 4. I think that the system ACL entry should be "hidden" and not
> displayed by ACL-list printing. I'm not quite sure yet how to make
> that happen.  It would be nicer if the owner ID could be passed to
> recursive_revoke out-of-band, instead of being represented inside the
> ACL list, but I don't see how to do that for all its callers.
>
> Thoughts?

(1) It seems to me that part of the consequence of what the suggest
    could be that there would be no such thing as default acl implied
    by a null entry in an aclitem. If so, this would be a very good thing.
    However, this has implications on pg initialization.

(2) Although I subscribe your first 3 points, I do not like the
    4th point. I don't think it is good practice to hide anything.
    That would make the acl display less understandable, as part
    for the reality is not shown. It makes any external tool
    (pgadmin, advisor, whatever else) to have to know this fact
    and possibly handle it as a special case.

(3) The standard name for the system grantor is "_SYSTEM". User number
    0 does not seem a bad idea, but how would it interact with number 1?
    How often in the source code will they have to be tested?

(4) How can/could a super user add or change these system granted
    privileges? Or should it be forbidden even to the su?

(5) Some thought could be given to the implication about future ROLEs.
    I'm interested in roles for my teaching, as they could allow
    a database owner to manage fully the rights of its database wrt
    to other users without needing any super user privilege.
    Good for students;-)

-- 
Fabien Coelho - coelho@cri.ensmp.fr

Sat, 15 May 2004 10:53:40 +0200 (MEST)
Fabien COELHO <coelho@cri.ensmp.fr>
Re: Bogus permissions display in 7.4 
Christopher Kings-Lynne wrote:
> Guess this means I have to tweak my ACL parser in phpPgAdmin.  If you
> could do something to make that less of a NIGHTMARE, i'd be all ears

Check out how the information schema views relating to privileges are 
implemented.  There are some undocumented accessor functions that you 
may find useful.



Sat, 15 May 2004 16:34:46 +0200
Peter Eisentraut <peter_e@gmx.net>
Re: Bogus permissions display in 7.4
Fabien COELHO <coelho@cri.ensmp.fr> writes:
>> 4. I think that the system ACL entry should be "hidden" and not
>> displayed by ACL-list printing. I'm not quite sure yet how to make
>> that happen.  It would be nicer if the owner ID could be passed to
>> recursive_revoke out-of-band, instead of being represented inside the
>> ACL list, but I don't see how to do that for all its callers.

> (1) It seems to me that part of the consequence of what the suggest
>     could be that there would be no such thing as default acl implied
>     by a null entry in an aclitem. If so, this would be a very good thing.
>     However, this has implications on pg initialization.

I have no big interest in changing that right now.  It doesn't impact
any of the points at issue.

> (2) Although I subscribe your first 3 points, I do not like the
>     4th point.

I didn't either.  After working on it some more, what I want to do now
is keep the ACL representation the same as it is, but implicitly assume
that the owner has all grant options whether the ACL says so or not.
The "other callers" I was referring to above are the undocumented
aclinsert() and aclremove() functions, which I propose changing to
add an owner's-UID parameter to.  Neither of these are actually used
anywhere, so it might make more sense to just remove 'em.  We'd also
need to change aclcontains(), which is used in the information_schema
views.

With this code base it would not really matter whether default ACLs
include the owner's grant options or not.  I am inclined to the view
that they should not, so as to minimize clutter in the \z display.
One could argue that the owner doesn't really have grant options in the
normal sense anyway, the normal sense being a revocable right, which
these are not.

I have a preliminary patch for this, which I will post later --- it
needs some more work yet.  (I only just realized the need to do
something with aclcontains... at the moment the information_schema
in my patched version is showing that the owner doesn't have grant
options, which is probably not what we want it to say.)

			regards, tom lane

Sat, 15 May 2004 13:37:20 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: Bogus permissions display in 7.4 
I wrote:
> I think the correct solution involves having recursive_revoke look to
> see if D still has the rights from somewhere else before it goes off to
> recursively revoke D's grants.  This makes it correctly implement the
> notion that one always has the union of the rights granted by anyone.

While implementing this I noticed a fine point, which is that we need a
defense to keep people from setting up irrevocable circular chains of
rights.  Consider:

1. A, the object owner, grants some privileges with grant options to B.

2. B re-grants the same to C.

3. C re-grants the same to B.

Now A effectively cannot revoke B's privileges, because recursive_revoke
will see that B still holds the grant from C, and not recurse.

B does not even need a partner in crime ... he can just re-grant to
himself with grant option, and he's got irrevocable rights.

Fortunately, it's not too hard for aclinsert to detect an attempt to set
up such a circular chain, and reject it.  What I have the code doing
when an attempt to grant grant options is made is (a) make a copy of the
ACL in which all grant options for the would-be grantee are removed with
DROP_CASCADE, then (b) examine this ACL to verify that the would-be
grantor still has grant options for the proposed rights.  If so, he
didn't derive the rights directly or indirectly from the grantee, and
the grant is not circular.

			regards, tom lane

Sat, 15 May 2004 13:52:25 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: Bogus permissions display in 7.4 

Dear Tom,

> > (2) Although I subscribe your first 3 points, I do not like the 4th point.
>
> I didn't either.  After working on it some more, what I want to do now
> is keep the ACL representation the same as it is, but implicitly assume
> that the owner has all grant options whether the ACL says so or not.

Mmmm... So you still want to stick to "exceptionnal" rights that are
managed somewhere explicitely in the backend code. I would much prefer
something explicit in the acl, because it would make the path to roles
easier, and I'm a little bit interested in this path.

What I was "implicitely" suggesting thru questions in my mail was an
approach where:

(0) all rights are always explicit, null means "no rights".
    So the algorithm to check accesses would be:

    - if (it is a super-user) access granted;
      else interpret explicitly ACL;

    Otherwise you have everywhere:

    - if (it is a super-user) access granted;
      elsif (it is the owner and we just need grant options) access granted;
      else interpret acl explicitly;

    As I've pointed out with some bug reports, the current middle section
    is quite buggy at the time, and I think I've found another one not yet
    reported bugs in this area.

    As we're dealing with security, the simpler the better. Having
    implicit things just make the code harder to understand and check
    because there is always a special case, and looking at the acl
    from userland needs some interpretation of things that are not there.

(1) "grant option" rights are given at creation time explicitly, maybe
    with your special user 0. They could then be revoked by the owner.

(2) if they are revoked, they could be given back but only by the
    super user, with something like:

    GRANT ALL ON ... TO calvin WITH GRANT OPTIONS FROM GRANTOR _SYSTEM;

It is a point that the super user should be able to create and
manage all rights, including explicit grantors.

So only the superuser is special in this approach, and owners are
only managed especially when creating an object, but not after.

This should make the actual code simpler and more explicit, so I feel that
it would less likely be buggy, and adding roles after that would be more
straightforward.

Well, all this is just my little opinion, and I'm not the one coding.

Have a nice day,

-- 
Fabien Coelho - coelho@cri.ensmp.fr

Mon, 17 May 2004 17:37:20 +0200 (CEST)
Fabien COELHO <coelho@cri.ensmp.fr>
Re: Bogus permissions display in 7.4 