Skip to content

Commit b59c03f

Browse files
committed
Make view/rule permission checking behave properly with
subqueries in the rule.
1 parent 7c57890 commit b59c03f

File tree

1 file changed

+126
-56
lines changed

1 file changed

+126
-56
lines changed

src/backend/rewrite/locks.c

Lines changed: 126 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.29 2000/05/30 00:49:51 momjian Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.30 2000/07/09 04:56:32 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -17,9 +17,9 @@
1717
#include "catalog/pg_shadow.h"
1818
#include "optimizer/clauses.h"
1919
#include "rewrite/locks.h"
20+
#include "parser/parsetree.h"
2021
#include "utils/acl.h"
2122
#include "utils/syscache.h"
22-
#include "utils/syscache.h"
2323

2424

2525
/*
@@ -152,93 +152,163 @@ matchLocks(CmdType event,
152152
}
153153

154154

155+
/*
156+
* Check the access permissions of tables that are referred to by a rule.
157+
* We want to check the access permissions using the userid of the rule's
158+
* owner, *not* of the current user (the one accessing the rule). So, we
159+
* do the permission check here and set skipAcl = TRUE in each of the rule's
160+
* RTEs, to prevent the executor from running another check with the current
161+
* user's ID.
162+
*
163+
* XXX This routine is called before the rule's query tree has been copied
164+
* out of the relcache entry where it is kept. Therefore, when we set
165+
* skipAcl = TRUE, we are destructively modifying the relcache entry for
166+
* the event relation! This seems fairly harmless because the relcache
167+
* querytree is only used as a source for the rewriter, but it's a tad
168+
* unclean anyway.
169+
*
170+
* Note that we must check permissions every time, even if skipAcl was
171+
* already set TRUE by a prior call. This ensures that we enforce the
172+
* current permission settings for each referenced table, even if they
173+
* have changed since the relcache entry was loaded.
174+
*/
175+
176+
typedef struct
177+
{
178+
char *evowner;
179+
} checkLockPerms_context;
180+
181+
static bool
182+
checkLockPerms_walker(Node *node,
183+
checkLockPerms_context *context)
184+
{
185+
if (node == NULL)
186+
return false;
187+
if (IsA(node, SubLink))
188+
{
189+
/*
190+
* Standard expression_tree_walker will not recurse into
191+
* subselect, but here we must do so.
192+
*/
193+
SubLink *sub = (SubLink *) node;
194+
195+
if (checkLockPerms_walker((Node *) (sub->lefthand), context))
196+
return true;
197+
if (checkLockPerms_walker((Node *) (sub->subselect), context))
198+
return true;
199+
return false;
200+
}
201+
if (IsA(node, Query))
202+
{
203+
/* Reach here after recursing down into subselect above... */
204+
Query *qry = (Query *) node;
205+
int rtablength = length(qry->rtable);
206+
int i;
207+
208+
/* Check all the RTEs in this query node, except OLD and NEW */
209+
for (i = 1; i <= rtablength; i++)
210+
{
211+
RangeTblEntry *rte = rt_fetch(i, qry->rtable);
212+
int32 reqperm;
213+
int32 aclcheck_res;
214+
215+
if (rte->ref != NULL)
216+
{
217+
if (strcmp(rte->ref->relname, "*NEW*") == 0)
218+
continue;
219+
if (strcmp(rte->ref->relname, "*OLD*") == 0)
220+
continue;
221+
}
222+
223+
if (i == qry->resultRelation)
224+
switch (qry->commandType)
225+
{
226+
case CMD_INSERT:
227+
reqperm = ACL_AP;
228+
break;
229+
default:
230+
reqperm = ACL_WR;
231+
break;
232+
}
233+
else
234+
reqperm = ACL_RD;
235+
236+
aclcheck_res = pg_aclcheck(rte->relname,
237+
context->evowner,
238+
reqperm);
239+
if (aclcheck_res != ACLCHECK_OK)
240+
elog(ERROR, "%s: %s",
241+
rte->relname,
242+
aclcheck_error_strings[aclcheck_res]);
243+
244+
/*
245+
* Mark RTE to prevent executor from checking again with the
246+
* current user's ID...
247+
*/
248+
rte->skipAcl = true;
249+
}
250+
251+
/* If there are sublinks, search for them and check their RTEs */
252+
if (qry->hasSubLinks)
253+
{
254+
if (checkLockPerms_walker((Node *) (qry->targetList), context))
255+
return true;
256+
if (checkLockPerms_walker((Node *) (qry->qual), context))
257+
return true;
258+
if (checkLockPerms_walker((Node *) (qry->havingQual), context))
259+
return true;
260+
}
261+
return false;
262+
}
263+
return expression_tree_walker(node, checkLockPerms_walker,
264+
(void *) context);
265+
}
266+
155267
void
156268
checkLockPerms(List *locks, Query *parsetree, int rt_index)
157269
{
270+
RangeTblEntry *rte;
158271
Relation ev_rel;
159272
HeapTuple usertup;
160-
char *evowner;
161-
RangeTblEntry *rte;
162-
int32 reqperm;
163-
int32 aclcheck_res;
164-
int i;
273+
Form_pg_shadow userform;
274+
checkLockPerms_context context;
165275
List *l;
166276

167277
if (locks == NIL)
168-
return;
278+
return; /* nothing to check */
169279

170280
/*
171-
* Get the usename of the rules event relation owner
281+
* Get the usename of the rule's event relation owner
172282
*/
173-
rte = (RangeTblEntry *) nth(rt_index - 1, parsetree->rtable);
283+
rte = rt_fetch(rt_index, parsetree->rtable);
174284
ev_rel = heap_openr(rte->relname, AccessShareLock);
175285
usertup = SearchSysCacheTuple(SHADOWSYSID,
176286
ObjectIdGetDatum(ev_rel->rd_rel->relowner),
177287
0, 0, 0);
178288
if (!HeapTupleIsValid(usertup))
179-
{
180289
elog(ERROR, "cache lookup for userid %d failed",
181290
ev_rel->rd_rel->relowner);
182-
}
291+
userform = (Form_pg_shadow) GETSTRUCT(usertup);
292+
context.evowner = pstrdup(NameStr(userform->usename));
183293
heap_close(ev_rel, AccessShareLock);
184-
evowner = pstrdup(NameStr(((Form_pg_shadow) GETSTRUCT(usertup))->usename));
185294

186295
/*
187-
* Check all the locks, that should get fired on this query
296+
* Check all the locks that should get fired on this query
188297
*/
189298
foreach(l, locks)
190299
{
191300
RewriteRule *onelock = (RewriteRule *) lfirst(l);
192301
List *action;
193302

194303
/*
195-
* In each lock check every action
304+
* In each lock check every action. We must scan the action
305+
* recursively in case there are any sub-queries within it.
196306
*/
197307
foreach(action, onelock->actions)
198308
{
199309
Query *query = (Query *) lfirst(action);
200310

201-
/*
202-
* In each action check every rangetable entry for read/write
203-
* permission of the event relations owner depending on if
204-
* it's the result relation (write) or not (read)
205-
*/
206-
for (i = 2; i < length(query->rtable); i++)
207-
{
208-
if (i + 1 == query->resultRelation)
209-
switch (query->resultRelation)
210-
{
211-
case CMD_INSERT:
212-
reqperm = ACL_AP;
213-
break;
214-
default:
215-
reqperm = ACL_WR;
216-
break;
217-
}
218-
else
219-
reqperm = ACL_RD;
220-
221-
rte = (RangeTblEntry *) nth(i, query->rtable);
222-
aclcheck_res = pg_aclcheck(rte->relname,
223-
evowner, reqperm);
224-
if (aclcheck_res != ACLCHECK_OK)
225-
{
226-
elog(ERROR, "%s: %s",
227-
rte->relname,
228-
aclcheck_error_strings[aclcheck_res]);
229-
}
230-
231-
/*
232-
* So this is allowed due to the permissions of the rules
233-
* event relation owner. But let's see if the next one too
234-
*/
235-
rte->skipAcl = TRUE;
236-
}
311+
checkLockPerms_walker((Node *) query, &context);
237312
}
238313
}
239-
240-
/*
241-
* Phew, that was close
242-
*/
243-
return;
244314
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy