Fix permission-checking bug reported by Tim Burgess 10-Feb-03 (this time
authorTom Lane
Wed, 14 Jan 2004 23:01:55 +0000 (23:01 +0000)
committerTom Lane
Wed, 14 Jan 2004 23:01:55 +0000 (23:01 +0000)
for sure...).  Rather than relying on the query context of a rangetable
entry to identify what permissions it wants checked, store a full AclMode
mask in each RTE, and check exactly those bits.  This allows an RTE
specifying, say, INSERT privilege on a view to be copied into a derived
UPDATE query without changing meaning.  Per recent discussion thread.
initdb forced due to change of stored rule representation.

18 files changed:
src/backend/commands/view.c
src/backend/executor/execMain.c
src/backend/executor/nodeSubplan.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/parser/analyze.c
src/backend/parser/parse_clause.c
src/backend/parser/parse_relation.c
src/backend/rewrite/rewriteDefine.c
src/backend/rewrite/rewriteHandler.c
src/include/catalog/catversion.h
src/include/executor/executor.h
src/include/nodes/parsenodes.h
src/include/parser/parse_clause.h
src/include/utils/acl.h

index 346867bbfc4b59d945674ad0d6ede4c9ea32f817..a8c3cb5ce07ca2d220c0c8c2f295928d4bb2381e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.80 2004/01/10 23:28:44 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.81 2004/01/14 23:01:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -297,8 +297,8 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
                                              makeAlias("*NEW*", NIL),
                                              false, false);
    /* Must override addRangeTableEntry's default access-check flags */
-   rt_entry1->checkForRead = false;
-   rt_entry2->checkForRead = false;
+   rt_entry1->requiredPerms = 0;
+   rt_entry2->requiredPerms = 0;
 
    new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
 
index 9d64c979e08706279b0f48c1bebf6d80350ad2fc..a340477c47ab36121e3b854dd86667e4afb07547 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.226 2004/01/10 23:28:44 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.227 2004/01/14 23:01:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -86,8 +86,8 @@ static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid,
           EState *estate);
 static TupleTableSlot *EvalPlanQualNext(EState *estate);
 static void EndEvalPlanQual(EState *estate);
-static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation);
-static void ExecCheckXactReadOnly(Query *parsetree, CmdType operation);
+static void ExecCheckRTEPerms(RangeTblEntry *rte);
+static void ExecCheckXactReadOnly(Query *parsetree);
 static void EvalPlanQualStart(evalPlanQual *epq, EState *estate,
                  evalPlanQual *priorepq);
 static void EvalPlanQualStop(evalPlanQual *epq);
@@ -136,8 +136,8 @@ ExecutorStart(QueryDesc *queryDesc, bool useCurrentSnapshot, bool explainOnly)
     * If the transaction is read-only, we need to check if any writes are
     * planned to non-temporary tables.
     */
-   if (!explainOnly)
-       ExecCheckXactReadOnly(queryDesc->parsetree, queryDesc->operation);
+   if (XactReadOnly && !explainOnly)
+       ExecCheckXactReadOnly(queryDesc->parsetree);
 
    /*
     * Build EState, switch into per-query memory context for startup.
@@ -351,7 +351,7 @@ ExecutorRewind(QueryDesc *queryDesc)
  *     Check access permissions for all relations listed in a range table.
  */
 void
-ExecCheckRTPerms(List *rangeTable, CmdType operation)
+ExecCheckRTPerms(List *rangeTable)
 {
    List       *lp;
 
@@ -359,7 +359,7 @@ ExecCheckRTPerms(List *rangeTable, CmdType operation)
    {
        RangeTblEntry *rte = lfirst(lp);
 
-       ExecCheckRTEPerms(rte, operation);
+       ExecCheckRTEPerms(rte);
    }
 }
 
@@ -368,18 +368,18 @@ ExecCheckRTPerms(List *rangeTable, CmdType operation)
  *     Check access permissions for a single RTE.
  */
 static void
-ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
+ExecCheckRTEPerms(RangeTblEntry *rte)
 {
+   AclMode     requiredPerms;
    Oid         relOid;
    AclId       userid;
-   AclResult   aclcheck_result;
 
    /*
     * If it's a subquery, recursively examine its rangetable.
     */
    if (rte->rtekind == RTE_SUBQUERY)
    {
-       ExecCheckRTPerms(rte->subquery->rtable, operation);
+       ExecCheckRTPerms(rte->subquery->rtable);
        return;
    }
 
@@ -391,6 +391,13 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
    if (rte->rtekind != RTE_RELATION)
        return;
 
+   /*
+    * No work if requiredPerms is empty.
+    */
+   requiredPerms = rte->requiredPerms;
+   if (requiredPerms == 0)
+       return;
+
    relOid = rte->relid;
 
    /*
@@ -404,77 +411,68 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
     */
    userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
 
-#define CHECK(MODE)        pg_class_aclcheck(relOid, userid, MODE)
+   /*
+    * For each bit in requiredPerms, apply the required check.  (We can't
+    * do this in one aclcheck call because aclcheck treats multiple bits
+    * as OR semantics, when we want AND.)
+    *
+    * We use a well-known cute trick for isolating the rightmost one-bit
+    * in a nonzero word.  See nodes/bitmapset.c for commentary.
+    */
+#define RIGHTMOST_ONE(x) ((int32) (x) & -((int32) (x)))
 
-   if (rte->checkForRead)
+   while (requiredPerms != 0)
    {
-       aclcheck_result = CHECK(ACL_SELECT);
-       if (aclcheck_result != ACLCHECK_OK)
-           aclcheck_error(aclcheck_result, ACL_KIND_CLASS,
-                          get_rel_name(relOid));
-   }
+       AclMode     thisPerm;
+       AclResult   aclcheck_result;
 
-   if (rte->checkForWrite)
-   {
-       /*
-        * Note: write access in a SELECT context means SELECT FOR UPDATE.
-        * Right now we don't distinguish that from true update as far as
-        * permissions checks are concerned.
-        */
-       switch (operation)
-       {
-           case CMD_INSERT:
-               aclcheck_result = CHECK(ACL_INSERT);
-               break;
-           case CMD_SELECT:
-           case CMD_UPDATE:
-               aclcheck_result = CHECK(ACL_UPDATE);
-               break;
-           case CMD_DELETE:
-               aclcheck_result = CHECK(ACL_DELETE);
-               break;
-           default:
-               elog(ERROR, "unrecognized operation code: %d",
-                    (int) operation);
-               aclcheck_result = ACLCHECK_OK;  /* keep compiler quiet */
-               break;
-       }
+       thisPerm = RIGHTMOST_ONE(requiredPerms);
+       requiredPerms &= ~thisPerm;
+
+       aclcheck_result = pg_class_aclcheck(relOid, userid, thisPerm);
        if (aclcheck_result != ACLCHECK_OK)
            aclcheck_error(aclcheck_result, ACL_KIND_CLASS,
                           get_rel_name(relOid));
    }
 }
 
+/*
+ * Check that the query does not imply any writes to non-temp tables.
+ */
 static void
-ExecCheckXactReadOnly(Query *parsetree, CmdType operation)
+ExecCheckXactReadOnly(Query *parsetree)
 {
-   if (!XactReadOnly)
-       return;
+   List       *lp;
 
-   /* CREATE TABLE AS or SELECT INTO */
-   if (operation == CMD_SELECT && parsetree->into != NULL)
+   /*
+    * CREATE TABLE AS or SELECT INTO?
+    *
+    * XXX should we allow this if the destination is temp?
+    */
+   if (parsetree->into != NULL)
        goto fail;
 
-   if (operation == CMD_DELETE || operation == CMD_INSERT
-       || operation == CMD_UPDATE)
+   /* Fail if write permissions are requested on any non-temp table */
+   foreach(lp, parsetree->rtable)
    {
-       List       *lp;
+       RangeTblEntry *rte = lfirst(lp);
 
-       foreach(lp, parsetree->rtable)
+       if (rte->rtekind == RTE_SUBQUERY)
        {
-           RangeTblEntry *rte = lfirst(lp);
+           ExecCheckXactReadOnly(rte->subquery);
+           continue;
+       }
 
-           if (rte->rtekind != RTE_RELATION)
-               continue;
+       if (rte->rtekind != RTE_RELATION)
+           continue;
 
-           if (!rte->checkForWrite)
-               continue;
+       if ((rte->requiredPerms & (~ACL_SELECT)) == 0)
+           continue;
 
-           if (isTempNamespace(get_rel_namespace(rte->relid)))
-               continue;
+       if (isTempNamespace(get_rel_namespace(rte->relid)))
+           continue;
 
-           goto fail;
-       }
+       goto fail;
    }
 
    return;
@@ -511,7 +509,7 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
     * rangetable here --- subplan RTEs will be checked during
     * ExecInitSubPlan().
     */
-   ExecCheckRTPerms(parseTree->rtable, operation);
+   ExecCheckRTPerms(parseTree->rtable);
 
    /*
     * get information from query descriptor
index da7d5915f20591b2976f908d2f7442cf0f22fdc1..1624f41fd540cf083cc241e467034f7f0262275a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.59 2003/11/29 19:51:48 pgsql Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.60 2004/01/14 23:01:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -670,10 +670,9 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
    MemoryContext oldcontext;
 
    /*
-    * Do access checking on the rangetable entries in the subquery. Here,
-    * we assume the subquery is a SELECT.
+    * Do access checking on the rangetable entries in the subquery.
     */
-   ExecCheckRTPerms(subplan->rtable, CMD_SELECT);
+   ExecCheckRTPerms(subplan->rtable);
 
    /*
     * initialize my state
index 5a340ebaa9493b3f2d641b7dde4cb718ec5f7476..39f454fd3c9d38350ff8f0e63a6719e1173523c7 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.276 2004/01/10 23:28:44 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.277 2004/01/14 23:01:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1258,8 +1258,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
    COPY_NODE_FIELD(eref);
    COPY_SCALAR_FIELD(inh);
    COPY_SCALAR_FIELD(inFromCl);
-   COPY_SCALAR_FIELD(checkForRead);
-   COPY_SCALAR_FIELD(checkForWrite);
+   COPY_SCALAR_FIELD(requiredPerms);
    COPY_SCALAR_FIELD(checkAsUser);
 
    return newnode;
index 932d79f31f48e1c1beaecf86175ee0295fac887b..7951fad039c629baf39ea0b7e18e5674d3cc5e38 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.214 2004/01/10 23:28:45 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.215 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1574,8 +1574,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
    COMPARE_NODE_FIELD(eref);
    COMPARE_SCALAR_FIELD(inh);
    COMPARE_SCALAR_FIELD(inFromCl);
-   COMPARE_SCALAR_FIELD(checkForRead);
-   COMPARE_SCALAR_FIELD(checkForWrite);
+   COMPARE_SCALAR_FIELD(requiredPerms);
    COMPARE_SCALAR_FIELD(checkAsUser);
 
    return true;
index ce80cae4bdbf8e6478a8f4a4be97d53596a0a708..cd1fde7b5b7ffcc2f0d11c23dd9081017ab284ac 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.229 2004/01/06 04:31:01 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.230 2004/01/14 23:01:55 tgl Exp $
  *
  * NOTES
  *   Every node type that can appear in stored rules' parsetrees *must*
@@ -1358,9 +1358,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
 
    WRITE_BOOL_FIELD(inh);
    WRITE_BOOL_FIELD(inFromCl);
-   WRITE_BOOL_FIELD(checkForRead);
-   WRITE_BOOL_FIELD(checkForWrite);
-   WRITE_OID_FIELD(checkAsUser);
+   WRITE_UINT_FIELD(requiredPerms);
+   WRITE_UINT_FIELD(checkAsUser);
 }
 
 static void
index bbfcb1b454eca204394a56eb7717493e613d244b..93c71fd224775e28ee8b51616bfba178d65b410b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.164 2004/01/07 18:56:26 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.165 2004/01/14 23:01:55 tgl Exp $
  *
  * NOTES
  *   Path and Plan nodes do not have any readfuncs support, because we
@@ -939,9 +939,8 @@ _readRangeTblEntry(void)
 
    READ_BOOL_FIELD(inh);
    READ_BOOL_FIELD(inFromCl);
-   READ_BOOL_FIELD(checkForRead);
-   READ_BOOL_FIELD(checkForWrite);
-   READ_OID_FIELD(checkAsUser);
+   READ_UINT_FIELD(requiredPerms);
+   READ_UINT_FIELD(checkAsUser);
 
    READ_DONE();
 }
index 2d724265f06dc698ec3a2fb0fbf9c89181aea881..50d5006a960d739cd2e4e1e0cbce6e0f292ec6c0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.111 2004/01/05 05:07:35 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.112 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -227,8 +227,7 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
     * it examines the parent's inheritlist entry.  There's no need to
     * check twice, so turn off access check bits in the original RTE.
     */
-   rte->checkForRead = false;
-   rte->checkForWrite = false;
+   rte->requiredPerms = 0;
 
    /*
     * Initialize to compute size estimates for whole inheritance tree
index 33f32c1b377fb6e3321aff44f22d484147f5dbc1..8962082134726540583f5dd8b863df082b5b4d8f 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.295 2004/01/11 04:58:17 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.296 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -472,7 +472,8 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
    /* set up range table with just the result rel */
    qry->resultRelation = setTargetTable(pstate, stmt->relation,
                              interpretInhOption(stmt->relation->inhOpt),
-                                        true);
+                                        true,
+                                        ACL_DELETE);
 
    qry->distinctClause = NIL;
 
@@ -539,7 +540,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
     * table is not added to the joinlist or namespace.
     */
    qry->resultRelation = setTargetTable(pstate, stmt->relation,
-                                        false, false);
+                                        false, false, ACL_INSERT);
 
    /*
     * Is it INSERT ... SELECT or INSERT ... VALUES?
@@ -1721,8 +1722,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
                                makeAlias("*NEW*", NIL),
                                false, true);
    /* Must override addRangeTableEntry's default access-check flags */
-   oldrte->checkForRead = false;
-   newrte->checkForRead = false;
+   oldrte->requiredPerms = 0;
+   newrte->requiredPerms = 0;
 
    /*
     * They must be in the namespace too for lookup purposes, but only add
@@ -1820,8 +1821,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
            newrte = addRangeTableEntry(sub_pstate, stmt->relation,
                                        makeAlias("*NEW*", NIL),
                                        false, false);
-           oldrte->checkForRead = false;
-           newrte->checkForRead = false;
+           oldrte->requiredPerms = 0;
+           newrte->requiredPerms = 0;
            addRTEtoQuery(sub_pstate, oldrte, false, true);
            addRTEtoQuery(sub_pstate, newrte, false, true);
 
@@ -2493,7 +2494,8 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 
    qry->resultRelation = setTargetTable(pstate, stmt->relation,
                              interpretInhOption(stmt->relation->inhOpt),
-                                        true);
+                                        true,
+                                        ACL_UPDATE);
 
    /*
     * the FROM clause is non-standard SQL syntax. We used to be able to
@@ -2880,7 +2882,7 @@ transformForUpdate(Query *qry, List *forUpdate)
                case RTE_RELATION:
                    if (!intMember(i, rowMarks))    /* avoid duplicates */
                        rowMarks = lappendi(rowMarks, i);
-                   rte->checkForWrite = true;
+                   rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                    break;
                case RTE_SUBQUERY:
                    /*
@@ -2915,7 +2917,7 @@ transformForUpdate(Query *qry, List *forUpdate)
                        case RTE_RELATION:
                            if (!intMember(i, rowMarks)) /* avoid duplicates */
                                rowMarks = lappendi(rowMarks, i);
-                           rte->checkForWrite = true;
+                           rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                            break;
                        case RTE_SUBQUERY:
                            /*
index d4e6747df6ff441fed81692bc03fb682da91191e..8b7be43af13414729dee092f60b738b733bb12bf 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.125 2003/11/29 19:51:51 pgsql Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.126 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -116,11 +116,14 @@ transformFromClause(ParseState *pstate, List *frmList)
  *   to check for namespace conflict; we assume that the namespace was
  *   initially empty in these cases.)
  *
+ *   Finally, we mark the relation as requiring the permissions specified
+ *   by requiredPerms.
+ *
  *   Returns the rangetable index of the target relation.
  */
 int
 setTargetTable(ParseState *pstate, RangeVar *relation,
-              bool inh, bool alsoSource)
+              bool inh, bool alsoSource, AclMode requiredPerms)
 {
    RangeTblEntry *rte;
    int         rtindex;
@@ -149,16 +152,15 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
    Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
 
    /*
-    * Override addRangeTableEntry's default checkForRead, and instead
-    * mark target table as requiring write access.
+    * Override addRangeTableEntry's default ACL_SELECT permissions check,
+    * and instead mark target table as requiring exactly the specified
+    * permissions.
     *
     * If we find an explicit reference to the rel later during parse
-    * analysis, scanRTEForColumn will change checkForRead to 'true'
-    * again.  That can't happen for INSERT but it is possible for UPDATE
-    * and DELETE.
+    * analysis, scanRTEForColumn will add the ACL_SELECT bit back again.
+    * That can't happen for INSERT but it is possible for UPDATE and DELETE.
     */
-   rte->checkForRead = false;
-   rte->checkForWrite = true;
+   rte->requiredPerms = requiredPerms;
 
    /*
     * If UPDATE/DELETE, add table to joinlist and namespace.
index 76caa60aeb24ab0f1045493d488be03ddc7dd05b..3e314bea963417675f3d3044d3c69aaac422c70d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.91 2003/11/29 19:51:52 pgsql Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.92 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -437,7 +437,7 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
  * nothing.  It might seem that we need to propagate the mark to all the
  * contained RTEs, but that is not necessary.  This is so because a join
  * expression can only appear in a FROM clause, and any table named in
- * FROM will be marked checkForRead from the beginning.
+ * FROM will be marked as requiring read access from the beginning.
  */
 static Node *
 scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
@@ -477,7 +477,8 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
                         errmsg("column reference \"%s\" is ambiguous",
                                colname)));
            result = (Node *) make_var(pstate, rte, attnum);
-           rte->checkForRead = true;
+           /* Require read access */
+           rte->requiredPerms |= ACL_SELECT;
        }
    }
 
@@ -504,7 +505,8 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
                                     0, 0))
            {
                result = (Node *) make_var(pstate, rte, attnum);
-               rte->checkForRead = true;
+               /* Require read access */
+               rte->requiredPerms |= ACL_SELECT;
            }
        }
    }
@@ -689,7 +691,7 @@ addRangeTableEntry(ParseState *pstate,
     * Flags:
     * - this RTE should be expanded to include descendant tables,
     * - this RTE is in the FROM clause,
-    * - this RTE should be checked for read/write access rights.
+    * - this RTE should be checked for appropriate access rights.
     *
     * The initial default on access checks is always check-for-READ-access,
     * which is the right thing for all except target tables.
@@ -697,10 +699,9 @@ addRangeTableEntry(ParseState *pstate,
     */
    rte->inh = inh;
    rte->inFromCl = inFromCl;
-   rte->checkForRead = true;
-   rte->checkForWrite = false;
 
-   rte->checkAsUser = InvalidOid;      /* not set-uid by default, either */
+   rte->requiredPerms = ACL_SELECT;
+   rte->checkAsUser = 0;           /* not set-uid by default, either */
 
    /*
     * Add completed RTE to pstate's range table list, but not to join
@@ -784,7 +785,7 @@ addRangeTableEntryForRelation(ParseState *pstate,
     * Flags:
     * - this RTE should be expanded to include descendant tables,
     * - this RTE is in the FROM clause,
-    * - this RTE should be checked for read/write access rights.
+    * - this RTE should be checked for appropriate access rights.
     *
     * The initial default on access checks is always check-for-READ-access,
     * which is the right thing for all except target tables.
@@ -792,10 +793,9 @@ addRangeTableEntryForRelation(ParseState *pstate,
     */
    rte->inh = inh;
    rte->inFromCl = inFromCl;
-   rte->checkForRead = true;
-   rte->checkForWrite = false;
 
-   rte->checkAsUser = InvalidOid;      /* not set-uid by default, either */
+   rte->requiredPerms = ACL_SELECT;
+   rte->checkAsUser = 0;           /* not set-uid by default, either */
 
    /*
     * Add completed RTE to pstate's range table list, but not to join
@@ -864,17 +864,16 @@ addRangeTableEntryForSubquery(ParseState *pstate,
     * Flags:
     * - this RTE should be expanded to include descendant tables,
     * - this RTE is in the FROM clause,
-    * - this RTE should be checked for read/write access rights.
+    * - this RTE should be checked for appropriate access rights.
     *
     * Subqueries are never checked for access rights.
     *----------
     */
    rte->inh = false;           /* never true for subqueries */
    rte->inFromCl = inFromCl;
-   rte->checkForRead = false;
-   rte->checkForWrite = false;
 
-   rte->checkAsUser = InvalidOid;
+   rte->requiredPerms = 0;
+   rte->checkAsUser = 0;
 
    /*
     * Add completed RTE to pstate's range table list, but not to join
@@ -1034,15 +1033,17 @@ addRangeTableEntryForFunction(ParseState *pstate,
     * Flags:
     * - this RTE should be expanded to include descendant tables,
     * - this RTE is in the FROM clause,
-    * - this RTE should be checked for read/write access rights.
+    * - this RTE should be checked for appropriate access rights.
+    *
+    * Functions are never checked for access rights (at least, not by
+    * the RTE permissions mechanism).
     *----------
     */
    rte->inh = false;           /* never true for functions */
    rte->inFromCl = inFromCl;
-   rte->checkForRead = true;
-   rte->checkForWrite = false;
 
-   rte->checkAsUser = InvalidOid;
+   rte->requiredPerms = 0;
+   rte->checkAsUser = 0;
 
    /*
     * Add completed RTE to pstate's range table list, but not to join
@@ -1095,17 +1096,16 @@ addRangeTableEntryForJoin(ParseState *pstate,
     * Flags:
     * - this RTE should be expanded to include descendant tables,
     * - this RTE is in the FROM clause,
-    * - this RTE should be checked for read/write access rights.
+    * - this RTE should be checked for appropriate access rights.
     *
     * Joins are never checked for access rights.
     *----------
     */
    rte->inh = false;           /* never true for joins */
    rte->inFromCl = inFromCl;
-   rte->checkForRead = false;
-   rte->checkForWrite = false;
 
-   rte->checkAsUser = InvalidOid;
+   rte->requiredPerms = 0;
+   rte->checkAsUser = 0;
 
    /*
     * Add completed RTE to pstate's range table list, but not to join
index 90497cf5b88f8450c2dbef15f30d99ed70a85023..f1cbe96fd2a2c206a3356f992f496b070e7f1f25 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.91 2003/11/29 19:51:55 pgsql Exp $
+ *   $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.92 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,7 @@
 
 
 static void setRuleCheckAsUser(Query *qry, AclId userid);
-static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
+static bool setRuleCheckAsUser_walker(Node *node, AclId *context);
 
 
 /*
@@ -494,8 +494,8 @@ DefineQueryRewrite(RuleStmt *stmt)
  * 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
+ * requiredPerms bits will always be zero.  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
@@ -528,7 +528,7 @@ setRuleCheckAsUser(Query *qry, AclId userid)
  * Expression-tree walker to find sublink queries
  */
 static bool
-setRuleCheckAsUser_walker(Node *node, Oid *context)
+setRuleCheckAsUser_walker(Node *node, AclId *context)
 {
    if (node == NULL)
        return false;
index 3f69110a36a8d8093c12938449769dced2461ce5..e66eb905f563a198f485b9872e80ffb95eaea900 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.132 2004/01/14 03:39:22 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.133 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -655,13 +655,11 @@ ApplyRetrieveRule(Query *parsetree,
     */
    subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
    Assert(subrte->relid == relation->rd_id);
-   subrte->checkForRead = rte->checkForRead;
-   subrte->checkForWrite = rte->checkForWrite;
+   subrte->requiredPerms = rte->requiredPerms;
    subrte->checkAsUser = rte->checkAsUser;
 
-   rte->checkForRead = false;  /* no permission check on subquery itself */
-   rte->checkForWrite = false;
-   rte->checkAsUser = InvalidOid;
+   rte->requiredPerms = 0;     /* no permission check on subquery itself */
+   rte->checkAsUser = 0;
 
    /*
     * FOR UPDATE of view?
@@ -713,7 +711,7 @@ markQueryForUpdate(Query *qry, bool skipOldNew)
        {
            if (!intMember(rti, qry->rowMarks))
                qry->rowMarks = lappendi(qry->rowMarks, rti);
-           rte->checkForWrite = true;
+           rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
        }
        else if (rte->rtekind == RTE_SUBQUERY)
        {
index 1910e33e3b112cb316e5eaea7ff1061830e62cef..7dd262c1e464f424a41acbfb4faf420a7dc3fd2b 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.215 2004/01/06 23:55:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.216 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200401061
+#define CATALOG_VERSION_NO 200401141
 
 #endif
index 8f1dc7fafaffb56001cb3ee98aa69eab1d23d61b..050894708c8bb57d104851eee87d4fa46d93beb3 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.104 2003/12/18 20:21:37 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.105 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -91,7 +91,7 @@ extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc,
            ScanDirection direction, long count);
 extern void ExecutorEnd(QueryDesc *queryDesc);
 extern void ExecutorRewind(QueryDesc *queryDesc);
-extern void ExecCheckRTPerms(List *rangeTable, CmdType operation);
+extern void ExecCheckRTPerms(List *rangeTable);
 extern void ExecEndPlan(PlanState *planstate, EState *estate);
 extern void ExecConstraints(ResultRelInfo *resultRelInfo,
                TupleTableSlot *slot, EState *estate);
index 8b6446d860518dd6ff2c004fbe5ac7c3d2e74fe5..01ff239a444548c9ffd915768f21c43d6cdb8dcd 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.252 2004/01/10 23:28:45 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.253 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,6 +27,32 @@ typedef enum QuerySource
    QSRC_NON_INSTEAD_RULE       /* added by non-INSTEAD rule */
 } QuerySource;
 
+/*
+ * Grantable rights are encoded so that we can OR them together in a bitmask.
+ * The present representation of AclItem limits us to 15 distinct rights,
+ * even though AclMode is defined as uint32.  See utils/acl.h.
+ *
+ * Caution: changing these codes breaks stored ACLs, hence forces initdb.
+ */
+typedef uint32 AclMode;            /* a bitmask of privilege bits */
+
+#define ACL_INSERT     (1<<0)  /* for relations */
+#define ACL_SELECT     (1<<1)
+#define ACL_UPDATE     (1<<2)
+#define ACL_DELETE     (1<<3)
+#define ACL_RULE       (1<<4)
+#define ACL_REFERENCES (1<<5)
+#define ACL_TRIGGER        (1<<6)
+#define ACL_EXECUTE        (1<<7)  /* for functions */
+#define ACL_USAGE      (1<<8)  /* for languages and namespaces */
+#define ACL_CREATE     (1<<9)  /* for namespaces and databases */
+#define ACL_CREATE_TEMP (1<<10) /* for databases */
+#define N_ACL_RIGHTS   11      /* 1 plus the last 1<
+#define ACL_ALL_RIGHTS (-1)    /* all-privileges marker in GRANT list */
+#define ACL_NO_RIGHTS  0
+/* Currently, SELECT ... FOR UPDATE requires UPDATE privileges */
+#define ACL_SELECT_FOR_UPDATE  ACL_UPDATE
+
 
 /*****************************************************************************
  * Query Tree
@@ -425,12 +451,13 @@ typedef struct DefElem
  *   column names processed later, and it also shouldn't affect the
  *   expansion of '*'.
  *
- *   checkForRead, checkForWrite, and checkAsUser control run-time access
- *   permissions checks.  A rel will be checked for read or write access
- *   (or both, or neither) per checkForRead and checkForWrite.  If
- *   checkAsUser is not InvalidOid, then do the permissions checks using
- *   the access rights of that user, not the current effective user ID.
- *   (This allows rules to act as setuid gateways.)
+ *   requiredPerms and checkAsUser specify run-time access permissions
+ *   checks to be performed at query startup.  The user must have *all*
+ *   of the permissions that are OR'd together in requiredPerms (zero
+ *   indicates no permissions checking).  If checkAsUser is not zero,
+ *   then do the permissions checks using the access rights of that user,
+ *   not the current effective user ID.  (This allows rules to act as
+ *   setuid gateways.)
  *--------------------
  */
 typedef enum RTEKind
@@ -490,9 +517,8 @@ typedef struct RangeTblEntry
    Alias      *eref;           /* expanded reference names */
    bool        inh;            /* inheritance requested? */
    bool        inFromCl;       /* present in FROM clause */
-   bool        checkForRead;   /* check rel for read access */
-   bool        checkForWrite;  /* check rel for write access */
-   Oid         checkAsUser;    /* if not zero, check access as this user */
+   AclMode     requiredPerms;  /* bitmask of required access permissions */
+   AclId       checkAsUser;    /* if not zero, check access as this user */
 } RangeTblEntry;
 
 /*
@@ -809,26 +835,6 @@ typedef enum GrantObjectType
    ACL_OBJECT_NAMESPACE        /* namespace */
 } GrantObjectType;
 
-/*
- * Grantable rights are encoded so that we can OR them together in a bitmask.
- * The present representation of AclItem limits us to 15 distinct rights.
- * Caution: changing these codes breaks stored ACLs, hence forces initdb.
- */
-#define ACL_INSERT     (1<<0)  /* for relations */
-#define ACL_SELECT     (1<<1)
-#define ACL_UPDATE     (1<<2)
-#define ACL_DELETE     (1<<3)
-#define ACL_RULE       (1<<4)
-#define ACL_REFERENCES (1<<5)
-#define ACL_TRIGGER        (1<<6)
-#define ACL_EXECUTE        (1<<7)  /* for functions */
-#define ACL_USAGE      (1<<8)  /* for languages and namespaces */
-#define ACL_CREATE     (1<<9)  /* for namespaces and databases */
-#define ACL_CREATE_TEMP (1<<10) /* for databases */
-#define N_ACL_RIGHTS   11      /* 1 plus the last 1<
-#define ACL_ALL_RIGHTS (-1)    /* all-privileges marker in GRANT list */
-#define ACL_NO_RIGHTS  0
-
 typedef struct GrantStmt
 {
    NodeTag     type;
index a57a80325b54826c4a8a3ecc2d56b9a25671888a..d91f5e80643ae5af28ec2dd433161f4c698c7c44 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_clause.h,v 1.38 2003/11/29 22:41:09 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_clause.h,v 1.39 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,7 @@
 
 extern void transformFromClause(ParseState *pstate, List *frmList);
 extern int setTargetTable(ParseState *pstate, RangeVar *relation,
-              bool inh, bool alsoSource);
+              bool inh, bool alsoSource, AclMode requiredPerms);
 extern bool interpretInhOption(InhOption inhOpt);
 
 extern Node *transformWhereClause(ParseState *pstate, Node *clause,
index be34fcce5ce12776a019964279ba4d42bc6d328e..efe7af30b202934de8078c68db648b91ff71ae90 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.65 2003/11/29 22:41:15 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.66 2004/01/14 23:01:55 tgl Exp $
  *
  * NOTES
  *   An ACL array is simply an array of AclItems, representing the union
 #include "utils/array.h"
 
 
-/* typedef AclId is declared in c.h */
+/*
+ * typedef AclId is declared in c.h
+ *
+ * typedef AclMode is declared in parsenodes.h, also the individual privilege
+ * bit meanings are defined there
+ */
 
 #define ACL_ID_WORLD   0       /* placeholder for id in a WORLD acl item */
 
 #define ACL_IDTYPE_UID         0x01    /* user id - from pg_shadow */
 #define ACL_IDTYPE_GID         0x02    /* group id - from pg_group */
 
-/*
- * AclMode     a bitmask of privilege bits
- */
-typedef uint32 AclMode;
-
 /*
  * AclItem
  *