Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit dad5bb0

Browse files
committed
Redo permissions-checking code so that it does the right thing at APPEND
nodes. The former version failed to check permissions of relations that were referenced in second and later clauses of UNIONs, and it did not check permissions of tables referenced via inheritance.
1 parent fd9ff86 commit dad5bb0

File tree

2 files changed

+246
-89
lines changed

2 files changed

+246
-89
lines changed

src/backend/executor/execMain.c

Lines changed: 245 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
*
2828
*
2929
* IDENTIFICATION
30-
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.109 2000/02/15 03:36:49 thomas Exp $
30+
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.110 2000/03/09 05:15:33 tgl Exp $
3131
*
3232
*-------------------------------------------------------------------------
3333
*/
@@ -46,9 +46,9 @@
4646
#include "utils/builtins.h"
4747
#include "utils/syscache.h"
4848

49-
void ExecCheckPerms(CmdType operation, int resultRelation, List *rangeTable,
50-
Query *parseTree);
51-
49+
/* XXX no points for style */
50+
extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
51+
ItemPointer tid);
5252

5353
/* decls for local routines only used within this module */
5454
static TupleDesc InitPlan(CmdType operation,
@@ -72,13 +72,18 @@ static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
7272
EState *estate);
7373
static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid,
7474
EState *estate);
75-
76-
TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ItemPointer tid);
7775
static TupleTableSlot *EvalPlanQualNext(EState *estate);
78-
79-
76+
static void ExecCheckQueryPerms(CmdType operation, Query *parseTree,
77+
Plan *plan);
78+
static void ExecCheckPlanPerms(Plan *plan, CmdType operation,
79+
int resultRelation, bool resultIsScanned);
80+
static void ExecCheckRTPerms(List *rangeTable, CmdType operation,
81+
int resultRelation, bool resultIsScanned);
82+
static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
83+
bool isResultRelation, bool resultIsScanned);
8084
/* end of local decls */
8185

86+
8287
/* ----------------------------------------------------------------
8388
* ExecutorStart
8489
*
@@ -378,104 +383,257 @@ ExecutorEnd(QueryDesc *queryDesc, EState *estate)
378383
}
379384
}
380385

381-
void
382-
ExecCheckPerms(CmdType operation,
383-
int resultRelation,
384-
List *rangeTable,
385-
Query *parseTree)
386+
387+
/*
388+
* ExecCheckQueryPerms
389+
* Check access permissions for all relations referenced in a query.
390+
*/
391+
static void
392+
ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
386393
{
387-
int rtindex = 0;
394+
List *rangeTable = parseTree->rtable;
395+
int resultRelation = parseTree->resultRelation;
396+
bool resultIsScanned = false;
388397
List *lp;
389-
List *qvars,
390-
*tvars;
391-
int32 ok = 1,
392-
aclcheck_result = -1;
393-
char *opstr;
394-
char *relName = NULL;
395-
char *userName;
396398

397-
#define CHECK(MODE) pg_aclcheck(relName, userName, MODE)
399+
/*
400+
* If we have a result relation, determine whether the result rel is
401+
* scanned or merely written. If scanned, we will insist on read
402+
* permission as well as modify permission.
403+
*/
404+
if (resultRelation > 0)
405+
{
406+
List *qvars = pull_varnos(parseTree->qual);
407+
List *tvars = pull_varnos((Node *) parseTree->targetList);
398408

399-
userName = GetPgUserName();
409+
resultIsScanned = (intMember(resultRelation, qvars) ||
410+
intMember(resultRelation, tvars));
411+
freeList(qvars);
412+
freeList(tvars);
413+
}
400414

401-
foreach(lp, rangeTable)
415+
/*
416+
* Check RTEs in the query's primary rangetable.
417+
*/
418+
ExecCheckRTPerms(rangeTable, operation, resultRelation, resultIsScanned);
419+
420+
/*
421+
* Check SELECT FOR UPDATE access rights.
422+
*/
423+
foreach(lp, parseTree->rowMark)
402424
{
403-
RangeTblEntry *rte = lfirst(lp);
425+
RowMark *rm = lfirst(lp);
404426

405-
++rtindex;
427+
if (!(rm->info & ROW_ACL_FOR_UPDATE))
428+
continue;
429+
430+
ExecCheckRTEPerms(rt_fetch(rm->rti, rangeTable),
431+
CMD_UPDATE, true, false);
432+
}
433+
434+
/*
435+
* Search for subplans and APPEND nodes to check their rangetables.
436+
*/
437+
ExecCheckPlanPerms(plan, operation, resultRelation, resultIsScanned);
438+
}
439+
440+
/*
441+
* ExecCheckPlanPerms
442+
* Recursively scan the plan tree to check access permissions in
443+
* subplans.
444+
*
445+
* We also need to look at the local rangetables in Append plan nodes,
446+
* which is pretty bogus --- most likely, those tables should be mentioned
447+
* in the query's main rangetable. But at the moment, they're not.
448+
*/
449+
static void
450+
ExecCheckPlanPerms(Plan *plan, CmdType operation,
451+
int resultRelation, bool resultIsScanned)
452+
{
453+
List *subp;
454+
455+
if (plan == NULL)
456+
return;
457+
458+
/* Check subplans, which we assume are plain SELECT queries */
459+
460+
foreach(subp, plan->initPlan)
461+
{
462+
SubPlan *subplan = (SubPlan *) lfirst(subp);
463+
464+
ExecCheckRTPerms(subplan->rtable, CMD_SELECT, 0, false);
465+
ExecCheckPlanPerms(subplan->plan, CMD_SELECT, 0, false);
466+
}
467+
foreach(subp, plan->subPlan)
468+
{
469+
SubPlan *subplan = (SubPlan *) lfirst(subp);
470+
471+
ExecCheckRTPerms(subplan->rtable, CMD_SELECT, 0, false);
472+
ExecCheckPlanPerms(subplan->plan, CMD_SELECT, 0, false);
473+
}
474+
475+
/* Check lower plan nodes */
476+
477+
ExecCheckPlanPerms(plan->lefttree, operation,
478+
resultRelation, resultIsScanned);
479+
ExecCheckPlanPerms(plan->righttree, operation,
480+
resultRelation, resultIsScanned);
481+
482+
/* Do node-type-specific checks */
406483

407-
if (rte->skipAcl)
484+
switch (nodeTag(plan))
485+
{
486+
case T_Append:
408487
{
488+
Append *app = (Append *) plan;
489+
List *appendplans;
409490

410-
/*
411-
* This happens if the access to this table is due to a view
412-
* query rewriting - the rewrite handler checked the
413-
* permissions against the view owner, so we just skip this
414-
* entry.
415-
*/
416-
continue;
417-
}
491+
if (app->inheritrelid > 0)
492+
{
493+
/*
494+
* Append implements expansion of inheritance; all members
495+
* of inheritrtable list will be plugged into same RTE slot.
496+
* Therefore, they are either all result relations or none.
497+
*/
498+
List *rtable;
418499

419-
relName = rte->relname;
420-
if (rtindex == resultRelation)
421-
{ /* this is the result relation */
422-
qvars = pull_varnos(parseTree->qual);
423-
tvars = pull_varnos((Node *) parseTree->targetList);
424-
if (intMember(resultRelation, qvars) ||
425-
intMember(resultRelation, tvars))
500+
foreach(rtable, app->inheritrtable)
501+
{
502+
ExecCheckRTEPerms((RangeTblEntry *) lfirst(rtable),
503+
operation,
504+
(app->inheritrelid == resultRelation),
505+
resultIsScanned);
506+
}
507+
}
508+
else
426509
{
427-
/* result relation is scanned */
428-
ok = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK);
429-
opstr = "read";
430-
if (!ok)
431-
break;
510+
/* Append implements UNION, which must be a SELECT */
511+
List *rtables;
512+
513+
foreach(rtables, app->unionrtables)
514+
{
515+
ExecCheckRTPerms((List *) lfirst(rtables),
516+
CMD_SELECT, 0, false);
517+
}
432518
}
433-
switch (operation)
519+
520+
/* Check appended plans */
521+
foreach(appendplans, app->appendplans)
434522
{
435-
case CMD_INSERT:
436-
ok = ((aclcheck_result = CHECK(ACL_AP)) == ACLCHECK_OK) ||
437-
((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK);
438-
opstr = "append";
439-
break;
440-
case CMD_DELETE:
441-
case CMD_UPDATE:
442-
ok = ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK);
443-
opstr = "write";
444-
break;
445-
default:
446-
elog(ERROR, "ExecCheckPerms: bogus operation %d",
447-
operation);
523+
ExecCheckPlanPerms((Plan *) lfirst(appendplans),
524+
operation,
525+
resultRelation,
526+
resultIsScanned);
448527
}
528+
break;
449529
}
450-
else
451-
{
452-
ok = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK);
453-
opstr = "read";
454-
}
455-
if (!ok)
530+
531+
default:
456532
break;
457533
}
458-
if (!ok)
459-
elog(ERROR, "%s: %s", relName, aclcheck_error_strings[aclcheck_result]);
534+
}
460535

461-
if (parseTree != NULL && parseTree->rowMark != NULL)
536+
/*
537+
* ExecCheckRTPerms
538+
* Check access permissions for all relations listed in a range table.
539+
*
540+
* If resultRelation is not 0, it is the RT index of the relation to be
541+
* treated as the result relation. All other relations are assumed to be
542+
* read-only for the query.
543+
*/
544+
static void
545+
ExecCheckRTPerms(List *rangeTable, CmdType operation,
546+
int resultRelation, bool resultIsScanned)
547+
{
548+
int rtindex = 0;
549+
List *lp;
550+
551+
foreach(lp, rangeTable)
462552
{
463-
foreach(lp, parseTree->rowMark)
464-
{
465-
RowMark *rm = lfirst(lp);
553+
RangeTblEntry *rte = lfirst(lp);
466554

467-
if (!(rm->info & ROW_ACL_FOR_UPDATE))
468-
continue;
555+
++rtindex;
556+
557+
ExecCheckRTEPerms(rte,
558+
operation,
559+
(rtindex == resultRelation),
560+
resultIsScanned);
561+
}
562+
}
563+
564+
/*
565+
* ExecCheckRTEPerms
566+
* Check access permissions for a single RTE.
567+
*/
568+
static void
569+
ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
570+
bool isResultRelation, bool resultIsScanned)
571+
{
572+
char *relName;
573+
char *userName;
574+
int32 aclcheck_result;
575+
576+
if (rte->skipAcl)
577+
{
578+
/*
579+
* This happens if the access to this table is due to a view
580+
* query rewriting - the rewrite handler already checked the
581+
* permissions against the view owner, so we just skip this entry.
582+
*/
583+
return;
584+
}
469585

470-
relName = rt_fetch(rm->rti, rangeTable)->relname;
471-
ok = ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK);
472-
opstr = "write";
473-
if (!ok)
474-
elog(ERROR, "%s: %s", relName, aclcheck_error_strings[aclcheck_result]);
586+
relName = rte->relname;
587+
588+
/*
589+
* Note: GetPgUserName is presently fast enough that there's no harm
590+
* in calling it separately for each RTE. If that stops being true,
591+
* we could call it once in ExecCheckQueryPerms and pass the userName
592+
* down from there. But for now, no need for the extra clutter.
593+
*/
594+
userName = GetPgUserName();
595+
596+
#define CHECK(MODE) pg_aclcheck(relName, userName, MODE)
597+
598+
if (isResultRelation)
599+
{
600+
if (resultIsScanned)
601+
{
602+
aclcheck_result = CHECK(ACL_RD);
603+
if (aclcheck_result != ACLCHECK_OK)
604+
elog(ERROR, "%s: %s",
605+
relName, aclcheck_error_strings[aclcheck_result]);
606+
}
607+
switch (operation)
608+
{
609+
case CMD_INSERT:
610+
/* Accept either APPEND or WRITE access for this */
611+
aclcheck_result = CHECK(ACL_AP);
612+
if (aclcheck_result != ACLCHECK_OK)
613+
aclcheck_result = CHECK(ACL_WR);
614+
break;
615+
case CMD_DELETE:
616+
case CMD_UPDATE:
617+
aclcheck_result = CHECK(ACL_WR);
618+
break;
619+
default:
620+
elog(ERROR, "ExecCheckRTEPerms: bogus operation %d",
621+
operation);
622+
aclcheck_result = ACLCHECK_OK; /* keep compiler quiet */
623+
break;
475624
}
476625
}
626+
else
627+
{
628+
aclcheck_result = CHECK(ACL_RD);
629+
}
630+
631+
if (aclcheck_result != ACLCHECK_OK)
632+
elog(ERROR, "%s: %s",
633+
relName, aclcheck_error_strings[aclcheck_result]);
477634
}
478635

636+
479637
/* ===============================================================
480638
* ===============================================================
481639
static routines follow
@@ -514,16 +672,19 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
514672
TupleDesc tupType;
515673
List *targetList;
516674

675+
/*
676+
* Do permissions checks.
677+
*/
678+
#ifndef NO_SECURITY
679+
ExecCheckQueryPerms(operation, parseTree, plan);
680+
#endif
681+
517682
/*
518683
* get information from query descriptor
519684
*/
520685
rangeTable = parseTree->rtable;
521686
resultRelation = parseTree->resultRelation;
522687

523-
#ifndef NO_SECURITY
524-
ExecCheckPerms(operation, resultRelation, rangeTable, parseTree);
525-
#endif
526-
527688
/*
528689
* initialize the node's execution state
529690
*/

0 commit comments

Comments
 (0)