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