1 | /* |
2 | * rewrite/rowsecurity.c |
3 | * Routines to support policies for row level security (aka RLS). |
4 | * |
5 | * Policies in PostgreSQL provide a mechanism to limit what records are |
6 | * returned to a user and what records a user is permitted to add to a table. |
7 | * |
8 | * Policies can be defined for specific roles, specific commands, or provided |
9 | * by an extension. Row security can also be enabled for a table without any |
10 | * policies being explicitly defined, in which case a default-deny policy is |
11 | * applied. |
12 | * |
13 | * Any part of the system which is returning records back to the user, or |
14 | * which is accepting records from the user to add to a table, needs to |
15 | * consider the policies associated with the table (if any). For normal |
16 | * queries, this is handled by calling get_row_security_policies() during |
17 | * rewrite, for each RTE in the query. This returns the expressions defined |
18 | * by the table's policies as a list that is prepended to the securityQuals |
19 | * list for the RTE. For queries which modify the table, any WITH CHECK |
20 | * clauses from the table's policies are also returned and prepended to the |
21 | * list of WithCheckOptions for the Query to check each row that is being |
22 | * added to the table. Other parts of the system (eg: COPY) simply construct |
23 | * a normal query and use that, if RLS is to be applied. |
24 | * |
25 | * The check to see if RLS should be enabled is provided through |
26 | * check_enable_rls(), which returns an enum (defined in rowsecurity.h) to |
27 | * indicate if RLS should be enabled (RLS_ENABLED), or bypassed (RLS_NONE or |
28 | * RLS_NONE_ENV). RLS_NONE_ENV indicates that RLS should be bypassed |
29 | * in the current environment, but that may change if the row_security GUC or |
30 | * the current role changes. |
31 | * |
32 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
33 | * Portions Copyright (c) 1994, Regents of the University of California |
34 | */ |
35 | #include "postgres.h" |
36 | |
37 | #include "access/htup_details.h" |
38 | #include "access/sysattr.h" |
39 | #include "access/table.h" |
40 | #include "catalog/pg_class.h" |
41 | #include "catalog/pg_inherits.h" |
42 | #include "catalog/pg_policy.h" |
43 | #include "catalog/pg_type.h" |
44 | #include "miscadmin.h" |
45 | #include "nodes/makefuncs.h" |
46 | #include "nodes/nodeFuncs.h" |
47 | #include "nodes/pg_list.h" |
48 | #include "nodes/plannodes.h" |
49 | #include "parser/parsetree.h" |
50 | #include "rewrite/rewriteDefine.h" |
51 | #include "rewrite/rewriteHandler.h" |
52 | #include "rewrite/rewriteManip.h" |
53 | #include "rewrite/rowsecurity.h" |
54 | #include "utils/acl.h" |
55 | #include "utils/lsyscache.h" |
56 | #include "utils/rel.h" |
57 | #include "utils/rls.h" |
58 | #include "utils/syscache.h" |
59 | #include "tcop/utility.h" |
60 | |
61 | static void get_policies_for_relation(Relation relation, |
62 | CmdType cmd, Oid user_id, |
63 | List **permissive_policies, |
64 | List **restrictive_policies); |
65 | |
66 | static List *sort_policies_by_name(List *policies); |
67 | |
68 | static int row_security_policy_cmp(const void *a, const void *b); |
69 | |
70 | static void add_security_quals(int rt_index, |
71 | List *permissive_policies, |
72 | List *restrictive_policies, |
73 | List **securityQuals, |
74 | bool *hasSubLinks); |
75 | |
76 | static void add_with_check_options(Relation rel, |
77 | int rt_index, |
78 | WCOKind kind, |
79 | List *permissive_policies, |
80 | List *restrictive_policies, |
81 | List **withCheckOptions, |
82 | bool *hasSubLinks, |
83 | bool force_using); |
84 | |
85 | static bool check_role_for_policy(ArrayType *policy_roles, Oid user_id); |
86 | |
87 | /* |
88 | * hooks to allow extensions to add their own security policies |
89 | * |
90 | * row_security_policy_hook_permissive can be used to add policies which |
91 | * are combined with the other permissive policies, using OR. |
92 | * |
93 | * row_security_policy_hook_restrictive can be used to add policies which |
94 | * are enforced, regardless of other policies (they are combined using AND). |
95 | */ |
96 | row_security_policy_hook_type row_security_policy_hook_permissive = NULL; |
97 | row_security_policy_hook_type row_security_policy_hook_restrictive = NULL; |
98 | |
99 | /* |
100 | * Get any row security quals and WithCheckOption checks that should be |
101 | * applied to the specified RTE. |
102 | * |
103 | * In addition, hasRowSecurity is set to true if row level security is enabled |
104 | * (even if this RTE doesn't have any row security quals), and hasSubLinks is |
105 | * set to true if any of the quals returned contain sublinks. |
106 | */ |
107 | void |
108 | get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, |
109 | List **securityQuals, List **withCheckOptions, |
110 | bool *hasRowSecurity, bool *hasSubLinks) |
111 | { |
112 | Oid user_id; |
113 | int rls_status; |
114 | Relation rel; |
115 | CmdType commandType; |
116 | List *permissive_policies; |
117 | List *restrictive_policies; |
118 | |
119 | /* Defaults for the return values */ |
120 | *securityQuals = NIL; |
121 | *withCheckOptions = NIL; |
122 | *hasRowSecurity = false; |
123 | *hasSubLinks = false; |
124 | |
125 | /* If this is not a normal relation, just return immediately */ |
126 | if (rte->relkind != RELKIND_RELATION && |
127 | rte->relkind != RELKIND_PARTITIONED_TABLE) |
128 | return; |
129 | |
130 | /* Switch to checkAsUser if it's set */ |
131 | user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); |
132 | |
133 | /* Determine the state of RLS for this, pass checkAsUser explicitly */ |
134 | rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); |
135 | |
136 | /* If there is no RLS on this table at all, nothing to do */ |
137 | if (rls_status == RLS_NONE) |
138 | return; |
139 | |
140 | /* |
141 | * RLS_NONE_ENV means we are not doing any RLS now, but that may change |
142 | * with changes to the environment, so we mark it as hasRowSecurity to |
143 | * force a re-plan when the environment changes. |
144 | */ |
145 | if (rls_status == RLS_NONE_ENV) |
146 | { |
147 | /* |
148 | * Indicate that this query may involve RLS and must therefore be |
149 | * replanned if the environment changes (GUCs, role), but we are not |
150 | * adding anything here. |
151 | */ |
152 | *hasRowSecurity = true; |
153 | |
154 | return; |
155 | } |
156 | |
157 | /* |
158 | * RLS is enabled for this relation. |
159 | * |
160 | * Get the security policies that should be applied, based on the command |
161 | * type. Note that if this isn't the target relation, we actually want |
162 | * the relation's SELECT policies, regardless of the query command type, |
163 | * for example in UPDATE t1 ... FROM t2 we need to apply t1's UPDATE |
164 | * policies and t2's SELECT policies. |
165 | */ |
166 | rel = table_open(rte->relid, NoLock); |
167 | |
168 | commandType = rt_index == root->resultRelation ? |
169 | root->commandType : CMD_SELECT; |
170 | |
171 | /* |
172 | * In some cases, we need to apply USING policies (which control the |
173 | * visibility of records) associated with multiple command types (see |
174 | * specific cases below). |
175 | * |
176 | * When considering the order in which to apply these USING policies, we |
177 | * prefer to apply higher privileged policies, those which allow the user |
178 | * to lock records (UPDATE and DELETE), first, followed by policies which |
179 | * don't (SELECT). |
180 | * |
181 | * Note that the optimizer is free to push down and reorder quals which |
182 | * use leakproof functions. |
183 | * |
184 | * In all cases, if there are no policy clauses allowing access to rows in |
185 | * the table for the specific type of operation, then a single |
186 | * always-false clause (a default-deny policy) will be added (see |
187 | * add_security_quals). |
188 | */ |
189 | |
190 | /* |
191 | * For a SELECT, if UPDATE privileges are required (eg: the user has |
192 | * specified FOR [KEY] UPDATE/SHARE), then add the UPDATE USING quals |
193 | * first. |
194 | * |
195 | * This way, we filter out any records from the SELECT FOR SHARE/UPDATE |
196 | * which the user does not have access to via the UPDATE USING policies, |
197 | * similar to how we require normal UPDATE rights for these queries. |
198 | */ |
199 | if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) |
200 | { |
201 | List *update_permissive_policies; |
202 | List *update_restrictive_policies; |
203 | |
204 | get_policies_for_relation(rel, CMD_UPDATE, user_id, |
205 | &update_permissive_policies, |
206 | &update_restrictive_policies); |
207 | |
208 | add_security_quals(rt_index, |
209 | update_permissive_policies, |
210 | update_restrictive_policies, |
211 | securityQuals, |
212 | hasSubLinks); |
213 | } |
214 | |
215 | /* |
216 | * For SELECT, UPDATE and DELETE, add security quals to enforce the USING |
217 | * policies. These security quals control access to existing table rows. |
218 | * Restrictive policies are combined together using AND, and permissive |
219 | * policies are combined together using OR. |
220 | */ |
221 | |
222 | get_policies_for_relation(rel, commandType, user_id, &permissive_policies, |
223 | &restrictive_policies); |
224 | |
225 | if (commandType == CMD_SELECT || |
226 | commandType == CMD_UPDATE || |
227 | commandType == CMD_DELETE) |
228 | add_security_quals(rt_index, |
229 | permissive_policies, |
230 | restrictive_policies, |
231 | securityQuals, |
232 | hasSubLinks); |
233 | |
234 | /* |
235 | * Similar to above, during an UPDATE or DELETE, if SELECT rights are also |
236 | * required (eg: when a RETURNING clause exists, or the user has provided |
237 | * a WHERE clause which involves columns from the relation), we collect up |
238 | * CMD_SELECT policies and add them via add_security_quals first. |
239 | * |
240 | * This way, we filter out any records which are not visible through an |
241 | * ALL or SELECT USING policy. |
242 | */ |
243 | if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) && |
244 | rte->requiredPerms & ACL_SELECT) |
245 | { |
246 | List *select_permissive_policies; |
247 | List *select_restrictive_policies; |
248 | |
249 | get_policies_for_relation(rel, CMD_SELECT, user_id, |
250 | &select_permissive_policies, |
251 | &select_restrictive_policies); |
252 | |
253 | add_security_quals(rt_index, |
254 | select_permissive_policies, |
255 | select_restrictive_policies, |
256 | securityQuals, |
257 | hasSubLinks); |
258 | } |
259 | |
260 | /* |
261 | * For INSERT and UPDATE, add withCheckOptions to verify that any new |
262 | * records added are consistent with the security policies. This will use |
263 | * each policy's WITH CHECK clause, or its USING clause if no explicit |
264 | * WITH CHECK clause is defined. |
265 | */ |
266 | if (commandType == CMD_INSERT || commandType == CMD_UPDATE) |
267 | { |
268 | /* This should be the target relation */ |
269 | Assert(rt_index == root->resultRelation); |
270 | |
271 | add_with_check_options(rel, rt_index, |
272 | commandType == CMD_INSERT ? |
273 | WCO_RLS_INSERT_CHECK : WCO_RLS_UPDATE_CHECK, |
274 | permissive_policies, |
275 | restrictive_policies, |
276 | withCheckOptions, |
277 | hasSubLinks, |
278 | false); |
279 | |
280 | /* |
281 | * Get and add ALL/SELECT policies, if SELECT rights are required for |
282 | * this relation (eg: when RETURNING is used). These are added as WCO |
283 | * policies rather than security quals to ensure that an error is |
284 | * raised if a policy is violated; otherwise, we might end up silently |
285 | * dropping rows to be added. |
286 | */ |
287 | if (rte->requiredPerms & ACL_SELECT) |
288 | { |
289 | List *select_permissive_policies = NIL; |
290 | List *select_restrictive_policies = NIL; |
291 | |
292 | get_policies_for_relation(rel, CMD_SELECT, user_id, |
293 | &select_permissive_policies, |
294 | &select_restrictive_policies); |
295 | add_with_check_options(rel, rt_index, |
296 | commandType == CMD_INSERT ? |
297 | WCO_RLS_INSERT_CHECK : WCO_RLS_UPDATE_CHECK, |
298 | select_permissive_policies, |
299 | select_restrictive_policies, |
300 | withCheckOptions, |
301 | hasSubLinks, |
302 | true); |
303 | } |
304 | |
305 | /* |
306 | * For INSERT ... ON CONFLICT DO UPDATE we need additional policy |
307 | * checks for the UPDATE which may be applied to the same RTE. |
308 | */ |
309 | if (commandType == CMD_INSERT && |
310 | root->onConflict && root->onConflict->action == ONCONFLICT_UPDATE) |
311 | { |
312 | List *conflict_permissive_policies; |
313 | List *conflict_restrictive_policies; |
314 | List *conflict_select_permissive_policies = NIL; |
315 | List *conflict_select_restrictive_policies = NIL; |
316 | |
317 | /* Get the policies that apply to the auxiliary UPDATE */ |
318 | get_policies_for_relation(rel, CMD_UPDATE, user_id, |
319 | &conflict_permissive_policies, |
320 | &conflict_restrictive_policies); |
321 | |
322 | /* |
323 | * Enforce the USING clauses of the UPDATE policies using WCOs |
324 | * rather than security quals. This ensures that an error is |
325 | * raised if the conflicting row cannot be updated due to RLS, |
326 | * rather than the change being silently dropped. |
327 | */ |
328 | add_with_check_options(rel, rt_index, |
329 | WCO_RLS_CONFLICT_CHECK, |
330 | conflict_permissive_policies, |
331 | conflict_restrictive_policies, |
332 | withCheckOptions, |
333 | hasSubLinks, |
334 | true); |
335 | |
336 | /* |
337 | * Get and add ALL/SELECT policies, as WCO_RLS_CONFLICT_CHECK WCOs |
338 | * to ensure they are considered when taking the UPDATE path of an |
339 | * INSERT .. ON CONFLICT DO UPDATE, if SELECT rights are required |
340 | * for this relation, also as WCO policies, again, to avoid |
341 | * silently dropping data. See above. |
342 | */ |
343 | if (rte->requiredPerms & ACL_SELECT) |
344 | { |
345 | get_policies_for_relation(rel, CMD_SELECT, user_id, |
346 | &conflict_select_permissive_policies, |
347 | &conflict_select_restrictive_policies); |
348 | add_with_check_options(rel, rt_index, |
349 | WCO_RLS_CONFLICT_CHECK, |
350 | conflict_select_permissive_policies, |
351 | conflict_select_restrictive_policies, |
352 | withCheckOptions, |
353 | hasSubLinks, |
354 | true); |
355 | } |
356 | |
357 | /* Enforce the WITH CHECK clauses of the UPDATE policies */ |
358 | add_with_check_options(rel, rt_index, |
359 | WCO_RLS_UPDATE_CHECK, |
360 | conflict_permissive_policies, |
361 | conflict_restrictive_policies, |
362 | withCheckOptions, |
363 | hasSubLinks, |
364 | false); |
365 | |
366 | /* |
367 | * Add ALL/SELECT policies as WCO_RLS_UPDATE_CHECK WCOs, to ensure |
368 | * that the final updated row is visible when taking the UPDATE |
369 | * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights |
370 | * are required for this relation. |
371 | */ |
372 | if (rte->requiredPerms & ACL_SELECT) |
373 | add_with_check_options(rel, rt_index, |
374 | WCO_RLS_UPDATE_CHECK, |
375 | conflict_select_permissive_policies, |
376 | conflict_select_restrictive_policies, |
377 | withCheckOptions, |
378 | hasSubLinks, |
379 | true); |
380 | } |
381 | } |
382 | |
383 | table_close(rel, NoLock); |
384 | |
385 | /* |
386 | * Copy checkAsUser to the row security quals and WithCheckOption checks, |
387 | * in case they contain any subqueries referring to other relations. |
388 | */ |
389 | setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); |
390 | setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); |
391 | |
392 | /* |
393 | * Mark this query as having row security, so plancache can invalidate it |
394 | * when necessary (eg: role changes) |
395 | */ |
396 | *hasRowSecurity = true; |
397 | |
398 | return; |
399 | } |
400 | |
401 | /* |
402 | * get_policies_for_relation |
403 | * |
404 | * Returns lists of permissive and restrictive policies to be applied to the |
405 | * specified relation, based on the command type and role. |
406 | * |
407 | * This includes any policies added by extensions. |
408 | */ |
409 | static void |
410 | get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id, |
411 | List **permissive_policies, |
412 | List **restrictive_policies) |
413 | { |
414 | ListCell *item; |
415 | |
416 | *permissive_policies = NIL; |
417 | *restrictive_policies = NIL; |
418 | |
419 | /* First find all internal policies for the relation. */ |
420 | foreach(item, relation->rd_rsdesc->policies) |
421 | { |
422 | bool cmd_matches = false; |
423 | RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item); |
424 | |
425 | /* Always add ALL policies, if they exist. */ |
426 | if (policy->polcmd == '*') |
427 | cmd_matches = true; |
428 | else |
429 | { |
430 | /* Check whether the policy applies to the specified command type */ |
431 | switch (cmd) |
432 | { |
433 | case CMD_SELECT: |
434 | if (policy->polcmd == ACL_SELECT_CHR) |
435 | cmd_matches = true; |
436 | break; |
437 | case CMD_INSERT: |
438 | if (policy->polcmd == ACL_INSERT_CHR) |
439 | cmd_matches = true; |
440 | break; |
441 | case CMD_UPDATE: |
442 | if (policy->polcmd == ACL_UPDATE_CHR) |
443 | cmd_matches = true; |
444 | break; |
445 | case CMD_DELETE: |
446 | if (policy->polcmd == ACL_DELETE_CHR) |
447 | cmd_matches = true; |
448 | break; |
449 | default: |
450 | elog(ERROR, "unrecognized policy command type %d" , |
451 | (int) cmd); |
452 | break; |
453 | } |
454 | } |
455 | |
456 | /* |
457 | * Add this policy to the relevant list of policies if it applies to |
458 | * the specified role. |
459 | */ |
460 | if (cmd_matches && check_role_for_policy(policy->roles, user_id)) |
461 | { |
462 | if (policy->permissive) |
463 | *permissive_policies = lappend(*permissive_policies, policy); |
464 | else |
465 | *restrictive_policies = lappend(*restrictive_policies, policy); |
466 | } |
467 | } |
468 | |
469 | /* |
470 | * We sort restrictive policies by name so that any WCOs they generate are |
471 | * checked in a well-defined order. |
472 | */ |
473 | *restrictive_policies = sort_policies_by_name(*restrictive_policies); |
474 | |
475 | /* |
476 | * Then add any permissive or restrictive policies defined by extensions. |
477 | * These are simply appended to the lists of internal policies, if they |
478 | * apply to the specified role. |
479 | */ |
480 | if (row_security_policy_hook_restrictive) |
481 | { |
482 | List *hook_policies = |
483 | (*row_security_policy_hook_restrictive) (cmd, relation); |
484 | |
485 | /* |
486 | * As with built-in restrictive policies, we sort any hook-provided |
487 | * restrictive policies by name also. Note that we also intentionally |
488 | * always check all built-in restrictive policies, in name order, |
489 | * before checking restrictive policies added by hooks, in name order. |
490 | */ |
491 | hook_policies = sort_policies_by_name(hook_policies); |
492 | |
493 | foreach(item, hook_policies) |
494 | { |
495 | RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item); |
496 | |
497 | if (check_role_for_policy(policy->roles, user_id)) |
498 | *restrictive_policies = lappend(*restrictive_policies, policy); |
499 | } |
500 | } |
501 | |
502 | if (row_security_policy_hook_permissive) |
503 | { |
504 | List *hook_policies = |
505 | (*row_security_policy_hook_permissive) (cmd, relation); |
506 | |
507 | foreach(item, hook_policies) |
508 | { |
509 | RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item); |
510 | |
511 | if (check_role_for_policy(policy->roles, user_id)) |
512 | *permissive_policies = lappend(*permissive_policies, policy); |
513 | } |
514 | } |
515 | } |
516 | |
517 | /* |
518 | * sort_policies_by_name |
519 | * |
520 | * This is only used for restrictive policies, ensuring that any |
521 | * WithCheckOptions they generate are applied in a well-defined order. |
522 | * This is not necessary for permissive policies, since they are all combined |
523 | * together using OR into a single WithCheckOption check. |
524 | */ |
525 | static List * |
526 | sort_policies_by_name(List *policies) |
527 | { |
528 | int npol = list_length(policies); |
529 | RowSecurityPolicy *pols; |
530 | ListCell *item; |
531 | int ii = 0; |
532 | |
533 | if (npol <= 1) |
534 | return policies; |
535 | |
536 | pols = (RowSecurityPolicy *) palloc(sizeof(RowSecurityPolicy) * npol); |
537 | |
538 | foreach(item, policies) |
539 | { |
540 | RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item); |
541 | |
542 | pols[ii++] = *policy; |
543 | } |
544 | |
545 | qsort(pols, npol, sizeof(RowSecurityPolicy), row_security_policy_cmp); |
546 | |
547 | policies = NIL; |
548 | for (ii = 0; ii < npol; ii++) |
549 | policies = lappend(policies, &pols[ii]); |
550 | |
551 | return policies; |
552 | } |
553 | |
554 | /* |
555 | * qsort comparator to sort RowSecurityPolicy entries by name |
556 | */ |
557 | static int |
558 | row_security_policy_cmp(const void *a, const void *b) |
559 | { |
560 | const RowSecurityPolicy *pa = (const RowSecurityPolicy *) a; |
561 | const RowSecurityPolicy *pb = (const RowSecurityPolicy *) b; |
562 | |
563 | /* Guard against NULL policy names from extensions */ |
564 | if (pa->policy_name == NULL) |
565 | return pb->policy_name == NULL ? 0 : 1; |
566 | if (pb->policy_name == NULL) |
567 | return -1; |
568 | |
569 | return strcmp(pa->policy_name, pb->policy_name); |
570 | } |
571 | |
572 | /* |
573 | * add_security_quals |
574 | * |
575 | * Add security quals to enforce the specified RLS policies, restricting |
576 | * access to existing data in a table. If there are no policies controlling |
577 | * access to the table, then all access is prohibited --- i.e., an implicit |
578 | * default-deny policy is used. |
579 | * |
580 | * New security quals are added to securityQuals, and hasSubLinks is set to |
581 | * true if any of the quals added contain sublink subqueries. |
582 | */ |
583 | static void |
584 | add_security_quals(int rt_index, |
585 | List *permissive_policies, |
586 | List *restrictive_policies, |
587 | List **securityQuals, |
588 | bool *hasSubLinks) |
589 | { |
590 | ListCell *item; |
591 | List *permissive_quals = NIL; |
592 | Expr *rowsec_expr; |
593 | |
594 | /* |
595 | * First collect up the permissive quals. If we do not find any |
596 | * permissive policies then no rows are visible (this is handled below). |
597 | */ |
598 | foreach(item, permissive_policies) |
599 | { |
600 | RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item); |
601 | |
602 | if (policy->qual != NULL) |
603 | { |
604 | permissive_quals = lappend(permissive_quals, |
605 | copyObject(policy->qual)); |
606 | *hasSubLinks |= policy->hassublinks; |
607 | } |
608 | } |
609 | |
610 | /* |
611 | * We must have permissive quals, always, or no rows are visible. |
612 | * |
613 | * If we do not, then we simply return a single 'false' qual which results |
614 | * in no rows being visible. |
615 | */ |
616 | if (permissive_quals != NIL) |
617 | { |
618 | /* |
619 | * We now know that permissive policies exist, so we can now add |
620 | * security quals based on the USING clauses from the restrictive |
621 | * policies. Since these need to be combined together using AND, we |
622 | * can just add them one at a time. |
623 | */ |
624 | foreach(item, restrictive_policies) |
625 | { |
626 | RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item); |
627 | Expr *qual; |
628 | |
629 | if (policy->qual != NULL) |
630 | { |
631 | qual = copyObject(policy->qual); |
632 | ChangeVarNodes((Node *) qual, 1, rt_index, 0); |
633 | |
634 | *securityQuals = list_append_unique(*securityQuals, qual); |
635 | *hasSubLinks |= policy->hassublinks; |
636 | } |
637 | } |
638 | |
639 | /* |
640 | * Then add a single security qual combining together the USING |
641 | * clauses from all the permissive policies using OR. |
642 | */ |
643 | if (list_length(permissive_quals) == 1) |
644 | rowsec_expr = (Expr *) linitial(permissive_quals); |
645 | else |
646 | rowsec_expr = makeBoolExpr(OR_EXPR, permissive_quals, -1); |
647 | |
648 | ChangeVarNodes((Node *) rowsec_expr, 1, rt_index, 0); |
649 | *securityQuals = list_append_unique(*securityQuals, rowsec_expr); |
650 | } |
651 | else |
652 | |
653 | /* |
654 | * A permissive policy must exist for rows to be visible at all. |
655 | * Therefore, if there were no permissive policies found, return a |
656 | * single always-false clause. |
657 | */ |
658 | *securityQuals = lappend(*securityQuals, |
659 | makeConst(BOOLOID, -1, InvalidOid, |
660 | sizeof(bool), BoolGetDatum(false), |
661 | false, true)); |
662 | } |
663 | |
664 | /* |
665 | * add_with_check_options |
666 | * |
667 | * Add WithCheckOptions of the specified kind to check that new records |
668 | * added by an INSERT or UPDATE are consistent with the specified RLS |
669 | * policies. Normally new data must satisfy the WITH CHECK clauses from the |
670 | * policies. If a policy has no explicit WITH CHECK clause, its USING clause |
671 | * is used instead. In the special case of an UPDATE arising from an |
672 | * INSERT ... ON CONFLICT DO UPDATE, existing records are first checked using |
673 | * a WCO_RLS_CONFLICT_CHECK WithCheckOption, which always uses the USING |
674 | * clauses from RLS policies. |
675 | * |
676 | * New WCOs are added to withCheckOptions, and hasSubLinks is set to true if |
677 | * any of the check clauses added contain sublink subqueries. |
678 | */ |
679 | static void |
680 | add_with_check_options(Relation rel, |
681 | int rt_index, |
682 | WCOKind kind, |
683 | List *permissive_policies, |
684 | List *restrictive_policies, |
685 | List **withCheckOptions, |
686 | bool *hasSubLinks, |
687 | bool force_using) |
688 | { |
689 | ListCell *item; |
690 | List *permissive_quals = NIL; |
691 | |
692 | #define QUAL_FOR_WCO(policy) \ |
693 | ( !force_using && \ |
694 | (policy)->with_check_qual != NULL ? \ |
695 | (policy)->with_check_qual : (policy)->qual ) |
696 | |
697 | /* |
698 | * First collect up the permissive policy clauses, similar to |
699 | * add_security_quals. |
700 | */ |
701 | foreach(item, permissive_policies) |
702 | { |
703 | RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item); |
704 | Expr *qual = QUAL_FOR_WCO(policy); |
705 | |
706 | if (qual != NULL) |
707 | { |
708 | permissive_quals = lappend(permissive_quals, copyObject(qual)); |
709 | *hasSubLinks |= policy->hassublinks; |
710 | } |
711 | } |
712 | |
713 | /* |
714 | * There must be at least one permissive qual found or no rows are allowed |
715 | * to be added. This is the same as in add_security_quals. |
716 | * |
717 | * If there are no permissive_quals then we fall through and return a |
718 | * single 'false' WCO, preventing all new rows. |
719 | */ |
720 | if (permissive_quals != NIL) |
721 | { |
722 | /* |
723 | * Add a single WithCheckOption for all the permissive policy clauses, |
724 | * combining them together using OR. This check has no policy name, |
725 | * since if the check fails it means that no policy granted permission |
726 | * to perform the update, rather than any particular policy being |
727 | * violated. |
728 | */ |
729 | WithCheckOption *wco; |
730 | |
731 | wco = makeNode(WithCheckOption); |
732 | wco->kind = kind; |
733 | wco->relname = pstrdup(RelationGetRelationName(rel)); |
734 | wco->polname = NULL; |
735 | wco->cascaded = false; |
736 | |
737 | if (list_length(permissive_quals) == 1) |
738 | wco->qual = (Node *) linitial(permissive_quals); |
739 | else |
740 | wco->qual = (Node *) makeBoolExpr(OR_EXPR, permissive_quals, -1); |
741 | |
742 | ChangeVarNodes(wco->qual, 1, rt_index, 0); |
743 | |
744 | *withCheckOptions = list_append_unique(*withCheckOptions, wco); |
745 | |
746 | /* |
747 | * Now add WithCheckOptions for each of the restrictive policy clauses |
748 | * (which will be combined together using AND). We use a separate |
749 | * WithCheckOption for each restrictive policy to allow the policy |
750 | * name to be included in error reports if the policy is violated. |
751 | */ |
752 | foreach(item, restrictive_policies) |
753 | { |
754 | RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item); |
755 | Expr *qual = QUAL_FOR_WCO(policy); |
756 | WithCheckOption *wco; |
757 | |
758 | if (qual != NULL) |
759 | { |
760 | qual = copyObject(qual); |
761 | ChangeVarNodes((Node *) qual, 1, rt_index, 0); |
762 | |
763 | wco = makeNode(WithCheckOption); |
764 | wco->kind = kind; |
765 | wco->relname = pstrdup(RelationGetRelationName(rel)); |
766 | wco->polname = pstrdup(policy->policy_name); |
767 | wco->qual = (Node *) qual; |
768 | wco->cascaded = false; |
769 | |
770 | *withCheckOptions = list_append_unique(*withCheckOptions, wco); |
771 | *hasSubLinks |= policy->hassublinks; |
772 | } |
773 | } |
774 | } |
775 | else |
776 | { |
777 | /* |
778 | * If there were no policy clauses to check new data, add a single |
779 | * always-false WCO (a default-deny policy). |
780 | */ |
781 | WithCheckOption *wco; |
782 | |
783 | wco = makeNode(WithCheckOption); |
784 | wco->kind = kind; |
785 | wco->relname = pstrdup(RelationGetRelationName(rel)); |
786 | wco->polname = NULL; |
787 | wco->qual = (Node *) makeConst(BOOLOID, -1, InvalidOid, |
788 | sizeof(bool), BoolGetDatum(false), |
789 | false, true); |
790 | wco->cascaded = false; |
791 | |
792 | *withCheckOptions = lappend(*withCheckOptions, wco); |
793 | } |
794 | } |
795 | |
796 | /* |
797 | * check_role_for_policy - |
798 | * determines if the policy should be applied for the current role |
799 | */ |
800 | static bool |
801 | check_role_for_policy(ArrayType *policy_roles, Oid user_id) |
802 | { |
803 | int i; |
804 | Oid *roles = (Oid *) ARR_DATA_PTR(policy_roles); |
805 | |
806 | /* Quick fall-thru for policies applied to all roles */ |
807 | if (roles[0] == ACL_ID_PUBLIC) |
808 | return true; |
809 | |
810 | for (i = 0; i < ARR_DIMS(policy_roles)[0]; i++) |
811 | { |
812 | if (has_privs_of_role(user_id, roles[i])) |
813 | return true; |
814 | } |
815 | |
816 | return false; |
817 | } |
818 | |