|
7 | 7 | *
|
8 | 8 | *
|
9 | 9 | * 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 $ |
11 | 11 | *
|
12 | 12 | *-------------------------------------------------------------------------
|
13 | 13 | */
|
|
17 | 17 | #include "catalog/pg_shadow.h"
|
18 | 18 | #include "optimizer/clauses.h"
|
19 | 19 | #include "rewrite/locks.h"
|
| 20 | +#include "parser/parsetree.h" |
20 | 21 | #include "utils/acl.h"
|
21 | 22 | #include "utils/syscache.h"
|
22 |
| -#include "utils/syscache.h" |
23 | 23 |
|
24 | 24 |
|
25 | 25 | /*
|
@@ -152,93 +152,163 @@ matchLocks(CmdType event,
|
152 | 152 | }
|
153 | 153 |
|
154 | 154 |
|
| 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 | + |
155 | 267 | void
|
156 | 268 | checkLockPerms(List *locks, Query *parsetree, int rt_index)
|
157 | 269 | {
|
| 270 | + RangeTblEntry *rte; |
158 | 271 | Relation ev_rel;
|
159 | 272 | 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; |
165 | 275 | List *l;
|
166 | 276 |
|
167 | 277 | if (locks == NIL)
|
168 |
| - return; |
| 278 | + return; /* nothing to check */ |
169 | 279 |
|
170 | 280 | /*
|
171 |
| - * Get the usename of the rules event relation owner |
| 281 | + * Get the usename of the rule's event relation owner |
172 | 282 | */
|
173 |
| - rte = (RangeTblEntry *) nth(rt_index - 1, parsetree->rtable); |
| 283 | + rte = rt_fetch(rt_index, parsetree->rtable); |
174 | 284 | ev_rel = heap_openr(rte->relname, AccessShareLock);
|
175 | 285 | usertup = SearchSysCacheTuple(SHADOWSYSID,
|
176 | 286 | ObjectIdGetDatum(ev_rel->rd_rel->relowner),
|
177 | 287 | 0, 0, 0);
|
178 | 288 | if (!HeapTupleIsValid(usertup))
|
179 |
| - { |
180 | 289 | elog(ERROR, "cache lookup for userid %d failed",
|
181 | 290 | ev_rel->rd_rel->relowner);
|
182 |
| - } |
| 291 | + userform = (Form_pg_shadow) GETSTRUCT(usertup); |
| 292 | + context.evowner = pstrdup(NameStr(userform->usename)); |
183 | 293 | heap_close(ev_rel, AccessShareLock);
|
184 |
| - evowner = pstrdup(NameStr(((Form_pg_shadow) GETSTRUCT(usertup))->usename)); |
185 | 294 |
|
186 | 295 | /*
|
187 |
| - * Check all the locks, that should get fired on this query |
| 296 | + * Check all the locks that should get fired on this query |
188 | 297 | */
|
189 | 298 | foreach(l, locks)
|
190 | 299 | {
|
191 | 300 | RewriteRule *onelock = (RewriteRule *) lfirst(l);
|
192 | 301 | List *action;
|
193 | 302 |
|
194 | 303 | /*
|
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. |
196 | 306 | */
|
197 | 307 | foreach(action, onelock->actions)
|
198 | 308 | {
|
199 | 309 | Query *query = (Query *) lfirst(action);
|
200 | 310 |
|
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); |
237 | 312 | }
|
238 | 313 | }
|
239 |
| - |
240 |
| - /* |
241 |
| - * Phew, that was close |
242 |
| - */ |
243 |
| - return; |
244 | 314 | }
|
0 commit comments