Thread: View permissions in 7.1

View permissions in 7.1

From
Lieven Van Acker
Date:
Hi,

I'm setting up a system to allow certain users to see only certain
records via a views. Thus, I revoked all permissions on the original
tables, I set up some views that handle the filtering on the records
using an account key, and these views I used to produce user views on
which permissions are granted to select, update , ...

In short, there are three layers of relations:

1. fysical tables: only access by sysadmin

ARE USED BY

2. administrative views: only access by sysadmin

ARE USED BY

3. user views: access to users depending on function

The problem is, that e.g. when I try to do a select on a user view, on
which a certain user has the permissions to select, I get an exception
that tells me the user has no rights to access to underlying
administrative views!

So from this behaviour, either I must have completely misunderstood the
authorization system, or their must be a bug in the system?

The following link drove me into the direction of seting the system up
like this.

http://www.archonet.com/pgdocs/chap-access.html#RESTRICT-USERS

Any comments will be greatly appreciated,

Lieven

A short example:

/* table to link uid to administrative accounts */
CREATE TABLE adm_user (login char(20), admin char(20));

/* sample base table */
CREATE TABLE base (admin char(20), data text);

/* sample administrative view */
CREATE VIEW adm_base AS
    SELECT b.data
    FROM base b, adm_user u
    WHERE
        (b.admin = u.admin) AND (u.login = bpchar(current_user))
;

/* rules to manipulate adm_base - omitted */

/* sample user view */
CREATE VIEW usr_base AS
    SELECT * FROM adm_base;

After setting up the adm_user table, granting permissions on  usr_base
to a user, and connecting to the DB as that user, I get

SELECT * FROM usr_base;
Error: adm_base: permission denied.

Of course, loosing the permissions on the adm_base view or base table
could solve this issue, but the point was security in implementing this
system!



Re: View permissions in 7.1

From
Tom Lane
Date:
Lieven Van Acker <lieven@elisa.be> writes:
> After setting up the adm_user table, granting permissions on  usr_base
> to a user, and connecting to the DB as that user, I get

> SELECT * FROM usr_base;
> Error: adm_base: permission denied.

This is a bug in 7.1 --- I'll see if I can fix it for 7.1.1.

            regards, tom lane

Re: View permissions in 7.1

From
Tom Lane
Date:
Lieven Van Acker <lieven@elisa.be> writes:
> [ permission checking doesn't work correctly for nested views in 7.1 ]

I think the attached patch fixes your problem; at least it fixes the
example you gave.  Do you have time to try it out more heavily before
Friday?  I'd like to commit it for 7.1.1 if it's right ...

            regards, tom lane

*** src/backend/rewrite/rewriteHandler.c.orig    Mon Apr 16 20:32:58 2001
--- src/backend/rewrite/rewriteHandler.c    Wed May  2 22:06:16 2001
***************
*** 309,317 ****
--- 309,319 ----
      Assert(subrte->relid == relation->rd_id);
      subrte->checkForRead = rte->checkForRead;
      subrte->checkForWrite = rte->checkForWrite;
+     subrte->checkAsUser = rte->checkAsUser;

      rte->checkForRead = false;    /* no permission check on subquery itself */
      rte->checkForWrite = false;
+     rte->checkAsUser = InvalidOid;

      /*
       * FOR UPDATE of view?

Re: View permissions in 7.1

From
Lieven Van Acker
Date:
Hi Tom,

I guess I was a bit to optimistic about the patch. It seems like the select permissions
are solved, but update (inc. insert / delete) operations still fail with permission
denied on the nested views.

Regards,

Lieven

Tom Lane wrote:

> Lieven Van Acker <lieven@elisa.be> writes:
> > [ permission checking doesn't work correctly for nested views in 7.1 ]
>
> I think the attached patch fixes your problem; at least it fixes the
> example you gave.  Do you have time to try it out more heavily before
> Friday?  I'd like to commit it for 7.1.1 if it's right ...
>
>                         regards, tom lane
>
> *** src/backend/rewrite/rewriteHandler.c.orig   Mon Apr 16 20:32:58 2001
> --- src/backend/rewrite/rewriteHandler.c        Wed May  2 22:06:16 2001
> ***************
> *** 309,317 ****
> --- 309,319 ----
>         Assert(subrte->relid == relation->rd_id);
>         subrte->checkForRead = rte->checkForRead;
>         subrte->checkForWrite = rte->checkForWrite;
> +       subrte->checkAsUser = rte->checkAsUser;
>
>         rte->checkForRead = false;      /* no permission check on subquery itself */
>         rte->checkForWrite = false;
> +       rte->checkAsUser = InvalidOid;
>
>         /*
>          * FOR UPDATE of view?
>
> ---------------------------(end of broadcast)---------------------------
> TIP 2: you can get off all lists at once with the unregister command
>     (send "unregister YourEmailAddressHere" to majordomo@postgresql.org)


Re: View permissions in 7.1

From
Tom Lane
Date:
Lieven Van Acker <lieven@elisa.be> writes:
> I guess I was a bit to optimistic about the patch. It seems like the
> select permissions are solved, but update (inc. insert / delete)
> operations still fail with permission denied on the nested views.

That's no help; I need an example.

            regards, tom lane

Re: View permissions in 7.1

From
Tom Lane
Date:
Lieven Van Acker <lieven@elisa.be> writes:
> I guess I was a bit to optimistic about the patch. It seems like the
> select permissions are solved, but update (inc. insert / delete)
> operations still fail with permission denied on the nested views.

Okay, the example you sent me off-list turns out to exhibit one bug
and one not-yet-implemented feature.  There is a bug in permissions
checking for insert/update/delete rules (any references therein to
NEW or OLD should be checked against the rule owner, not the calling
user).  A patch for that is attached.  However, you were also expecting
that an SQL function call would provide "setuid" behavior, and it
doesn't.  (I believe changing that is on the TODO list.)  In the
meantime, you'd need to replace the current_adm() function call in your
adm_base view rules with explicit subselects, so that the accesses to
the "users" table are checked against the rule owner rather than the
calling user.

BTW, this patch changes the stored form of a rule, so you'd need to
drop and recreate the rule to make it take effect.

            regards, tom lane


*** src/backend/rewrite/rewriteDefine.c.orig    Fri Mar 23 13:45:24 2001
--- src/backend/rewrite/rewriteDefine.c    Thu May  3 17:16:48 2001
***************
*** 8,14 ****
   *
   *
   * IDENTIFICATION
!  *      $Header: /home/projects/pgsql/cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.61 2001/03/23 04:49:54
momjianExp $ 
   *
   *-------------------------------------------------------------------------
   */
--- 8,14 ----
   *
   *
   * IDENTIFICATION
!  *      $Header: /home/projects/pgsql/cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.62 2001/05/03 21:16:48
tglExp $ 
   *
   *-------------------------------------------------------------------------
   */
***************
*** 377,383 ****
       * 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 (except *OLD* and *NEW*).
       */
      foreach(l, action)
      {
--- 377,383 ----
       * 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.
       */
      foreach(l, action)
      {
***************
*** 426,454 ****
  /*
   * setRuleCheckAsUser
   *        Recursively scan a query and set the checkAsUser field to the
!  *        given userid in all rtable entries except *OLD* and *NEW*.
   */
  static void
  setRuleCheckAsUser(Query *qry, Oid userid)
  {
      List       *l;

!     /* Set all the RTEs in this query node, except OLD and NEW */
      foreach(l, qry->rtable)
      {
          RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);

-         if (strcmp(rte->eref->relname, "*NEW*") == 0)
-             continue;
-         if (strcmp(rte->eref->relname, "*OLD*") == 0)
-             continue;
-
          if (rte->subquery)
          {
!
!             /*
!              * Recurse into subquery in FROM
!              */
              setRuleCheckAsUser(rte->subquery, userid);
          }
          else
--- 426,453 ----
  /*
   * setRuleCheckAsUser
   *        Recursively scan a query and set the checkAsUser field to the
!  *        given userid in all rtable entries.
!  *
!  * Note: for a view (ON SELECT rule), the checkAsUser field of the *OLD*
!  * RTE entry will be overridden when the view rule is expanded, and the
!  * checkAsUser field of the *NEW* entry is irrelevant because that entry's
!  * checkFor bits will never be set.  However, for other types of rules it's
!  * important to set these fields to match the rule owner.  So we just set
!  * them always.
   */
  static void
  setRuleCheckAsUser(Query *qry, Oid userid)
  {
      List       *l;

!     /* Set all the RTEs in this query node */
      foreach(l, qry->rtable)
      {
          RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);

          if (rte->subquery)
          {
!             /* Recurse into subquery in FROM */
              setRuleCheckAsUser(rte->subquery, userid);
          }
          else

Re: View permissions in 7.1

From
Tom Lane
Date:
Lieven Van Acker <lieven@elisa.be> writes:
> do I have to restore the original rewriteHandler.c? (before the first patch)

No, that patch is correct as far as it goes, and indeed necessary for
the second patch.

            regards, tom lane

Re: View permissions in 7.1

From
Lieven Van Acker
Date:
> Okay, the example you sent me off-list turns out to exhibit one bug
> and one not-yet-implemented feature.  There is a bug in permissions
> checking for insert/update/delete rules (any references therein to
> NEW or OLD should be checked against the rule owner, not the calling
> user).  A patch for that is attached.

Thanks, I'll apply it.

> However, you were also expecting
> that an SQL function call would provide "setuid" behavior, and it
> doesn't.  (I believe changing that is on the TODO list.)  In the
> meantime, you'd need to replace the current_adm() function call in your
> adm_base view rules with explicit subselects, so that the accesses to
> the "users" table are checked against the rule owner rather than the
> calling user.

Well, in fact, -at this point - I don't need setuid, because the function current_adm() has to lookup the effective uid
ofthe calling 
user. The point is I want to filter the records depending on the uid of the user calling the top-level view. So as I
canunderstand, 
views that are called by other views run still within the same session - thus returning the effective uid, right?

Kind Regards,

Lieven.


Re: View permissions in 7.1

From
Lieven Van Acker
Date:
Tom,

do I have to restore the original rewriteHandler.c? (before the first patch)

Lieven


Re: View permissions in 7.1

From
Tom Lane
Date:
Lieven Van Acker <lieven@elisa.be> writes:
> Well, in fact, -at this point - I don't need setuid, because the
> function current_adm() has to lookup the effective uid of the calling
> user. The point is I want to filter the records depending on the uid
> of the user calling the top-level view. So as I can understand, views
> that are called by other views run still within the same session -
> thus returning the effective uid, right?

The problem is that current_adm() fails for lack of read access on the
users table, when it's invoked on behalf of the unprivileged user.

I think that what you really want to be using for the lookup is
SESSION_USER not CURRENT_USER.  There's no difference at the moment,
but there will be once we have setuid functions ...

            regards, tom lane

Re: View permissions in 7.1

From
Lieven Van Acker
Date:

Tom Lane wrote:

> Lieven Van Acker <lieven@elisa.be> writes:
> > Well, in fact, -at this point - I don't need setuid, because the
> > function current_adm() has to lookup the effective uid of the calling
> > user. The point is I want to filter the records depending on the uid
> > of the user calling the top-level view. So as I can understand, views
> > that are called by other views run still within the same session -
> > thus returning the effective uid, right?

>
> The problem is that current_adm() fails for lack of read access on the
> users table, when it's invoked on behalf of the unprivileged user.
>

You're right. I forgot to grant select priv's to public!

>
> I think that what you really want to be using for the lookup is
> SESSION_USER not CURRENT_USER.  There's no difference at the moment,
> but there will be once we have setuid functions ...
>

Thanks for pointing this out. I'll have to change this to use the session_user!


>
>                         regards, tom lane