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