From: Tom Lane Date: Wed, 14 Jan 2004 23:01:55 +0000 (+0000) Subject: Fix permission-checking bug reported by Tim Burgess 10-Feb-03 (this time X-Git-Tag: REL9_0_0~13515 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=cfd7fb7ed4b66da97f88338d991843fa7e2fe59d;p=pg-rex%2Fsyncrep.git Fix permission-checking bug reported by Tim Burgess 10-Feb-03 (this time 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. --- diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 346867bbfc..a8c3cb5ce0 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -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)); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 9d64c979e0..a340477c47 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -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 diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index da7d5915f2..1624f41fd5 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -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 diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 5a340ebaa9..39f454fd3c 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 932d79f31f..7951fad039 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index ce80cae4bd..cd1fde7b5b 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -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 diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index bbfcb1b454..93c71fd224 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -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(); } diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 2d724265f0..50d5006a96 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -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 diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 33f32c1b37..8962082134 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -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: /* diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index d4e6747df6..8b7be43af1 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -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. diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 76caa60aeb..3e314bea96 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -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 diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 90497cf5b8..f1cbe96fd2 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -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; diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 3f69110a36..e66eb905f5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -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) { diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 1910e33e3b..7dd262c1e4 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -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 diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 8f1dc7fafa..050894708c 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -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); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 8b6446d860..01ff239a44 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -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<