Branch data 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 : 303556 : 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 : 303556 : *securityQuals = NIL;
112 : 303556 : *withCheckOptions = NIL;
113 : 303556 : *hasRowSecurity = false;
114 : 303556 : *hasSubLinks = false;
115 : :
116 : : Assert(rte->rtekind == RTE_RELATION);
117 : :
118 : : /* If this is not a normal relation, just return immediately */
119 [ + + ]: 303556 : if (rte->relkind != RELKIND_RELATION &&
120 [ - + ]: 12072 : rte->relkind != RELKIND_PARTITIONED_TABLE)
121 : 301257 : return;
122 : :
123 : 303556 : perminfo = getRTEPermissionInfo(root->rteperminfos, rte);
124 : :
125 : : /* Switch to checkAsUser if it's set */
126 : 607112 : user_id = OidIsValid(perminfo->checkAsUser) ?
127 [ + + ]: 303556 : perminfo->checkAsUser : GetUserId();
128 : :
129 : : /* Determine the state of RLS for this, pass checkAsUser explicitly */
130 : 303556 : 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 [ + + ]: 303500 : if (rls_status == RLS_NONE)
134 : 300767 : 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 [ + + ]: 2733 : 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 : 490 : *hasRowSecurity = true;
149 : :
150 : 490 : 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 : 2243 : rel = table_open(rte->relid, NoLock);
163 : :
164 : 4486 : commandType = rt_index == root->resultRelation ?
165 [ + + ]: 2243 : 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 [ + + + + ]: 2243 : if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE)
196 : : {
197 : : List *update_permissive_policies;
198 : : List *update_restrictive_policies;
199 : :
200 : 32 : get_policies_for_relation(rel, CMD_UPDATE, user_id,
201 : : &update_permissive_policies,
202 : : &update_restrictive_policies);
203 : :
204 : 32 : 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 : 2243 : get_policies_for_relation(rel, commandType, user_id, &permissive_policies,
219 : : &restrictive_policies);
220 : :
221 [ + + + + ]: 2243 : if (commandType == CMD_SELECT ||
222 [ + + ]: 466 : commandType == CMD_UPDATE ||
223 : : commandType == CMD_DELETE)
224 : 1861 : 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 [ + + + + : 2243 : if ((commandType == CMD_UPDATE || commandType == CMD_DELETE ||
+ + ]
241 : 392 : commandType == CMD_MERGE) &&
242 [ + + ]: 392 : perminfo->requiredPerms & ACL_SELECT)
243 : : {
244 : : List *select_permissive_policies;
245 : : List *select_restrictive_policies;
246 : :
247 : 364 : get_policies_for_relation(rel, CMD_SELECT, user_id,
248 : : &select_permissive_policies,
249 : : &select_restrictive_policies);
250 : :
251 : 364 : 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 [ + + + + ]: 2243 : if (commandType == CMD_INSERT || commandType == CMD_UPDATE)
265 : : {
266 : : /* This should be the target relation */
267 : : Assert(rt_index == root->resultRelation);
268 : :
269 [ + + ]: 458 : 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 [ + + ]: 458 : if (perminfo->requiredPerms & ACL_SELECT)
286 : : {
287 : 316 : List *select_permissive_policies = NIL;
288 : 316 : List *select_restrictive_policies = NIL;
289 : :
290 : 316 : get_policies_for_relation(rel, CMD_SELECT, user_id,
291 : : &select_permissive_policies,
292 : : &select_restrictive_policies);
293 [ + + ]: 316 : 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 [ + + + + ]: 458 : if (commandType == CMD_INSERT && root->onConflict &&
309 [ + + ]: 128 : (root->onConflict->action == ONCONFLICT_UPDATE ||
310 [ + + ]: 48 : root->onConflict->action == ONCONFLICT_SELECT))
311 : : {
312 : 116 : List *conflict_permissive_policies = NIL;
313 : 116 : List *conflict_restrictive_policies = NIL;
314 : 116 : List *conflict_select_permissive_policies = NIL;
315 : 116 : List *conflict_select_restrictive_policies = NIL;
316 : :
317 [ + + ]: 116 : 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 : 96 : 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 : 96 : 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 [ + - ]: 116 : if (perminfo->requiredPerms & ACL_SELECT)
350 : : {
351 : 116 : get_policies_for_relation(rel, CMD_SELECT, user_id,
352 : : &conflict_select_permissive_policies,
353 : : &conflict_select_restrictive_policies);
354 : 116 : 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 [ + + ]: 116 : if (root->onConflict->action == ONCONFLICT_UPDATE)
368 : : {
369 : : /* Enforce the WITH CHECK clauses of the UPDATE policies */
370 : 80 : 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 [ + - ]: 80 : if (perminfo->requiredPerms & ACL_SELECT)
385 : 80 : 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 : : * UPDATE/DELETE FOR PORTION OF may insert leftover rows to preserve the
398 : : * portions of the old row not covered by the target range. Those hidden
399 : : * inserts go through ExecInsert(), so they need the same INSERT RLS WITH
400 : : * CHECK options as ordinary INSERTs. SELECT rights are never needed for
401 : : * the leftover rows, because they are not considered by RETURNING.
402 : : */
403 [ + + + - : 2243 : if (root->forPortionOf != NULL && rt_index == root->resultRelation &&
+ + ]
404 [ + - ]: 12 : (commandType == CMD_UPDATE || commandType == CMD_DELETE))
405 : : {
406 : : List *insert_permissive_policies;
407 : : List *insert_restrictive_policies;
408 : :
409 : 24 : get_policies_for_relation(rel, CMD_INSERT, user_id,
410 : : &insert_permissive_policies,
411 : : &insert_restrictive_policies);
412 : 24 : add_with_check_options(rel, rt_index,
413 : : WCO_RLS_INSERT_CHECK,
414 : : insert_permissive_policies,
415 : : insert_restrictive_policies,
416 : : withCheckOptions,
417 : : hasSubLinks,
418 : : false);
419 : : }
420 : :
421 : : /*
422 : : * FOR MERGE, we fetch policies for UPDATE, DELETE and INSERT (and ALL)
423 : : * and set them up so that we can enforce the appropriate policy depending
424 : : * on the final action we take.
425 : : *
426 : : * We already fetched the SELECT policies above, to check existing rows,
427 : : * but we must also check that new rows created by INSERT/UPDATE actions
428 : : * are visible, if SELECT rights are required. For INSERT actions, we only
429 : : * do this if RETURNING is specified, to be consistent with a plain INSERT
430 : : * command, which can only require SELECT rights when RETURNING is used.
431 : : *
432 : : * We don't push the UPDATE/DELETE USING quals to the RTE because we don't
433 : : * really want to apply them while scanning the relation since we don't
434 : : * know whether we will be doing an UPDATE or a DELETE at the end. We
435 : : * apply the respective policy once we decide the final action on the
436 : : * target tuple.
437 : : *
438 : : * XXX We are setting up USING quals as WITH CHECK. If RLS prohibits
439 : : * UPDATE/DELETE on the target row, we shall throw an error instead of
440 : : * silently ignoring the row. This is different than how normal
441 : : * UPDATE/DELETE works and more in line with INSERT ON CONFLICT DO
442 : : * SELECT/UPDATE handling.
443 : : */
444 [ + + ]: 2243 : if (commandType == CMD_MERGE)
445 : : {
446 : : List *merge_update_permissive_policies;
447 : : List *merge_update_restrictive_policies;
448 : : List *merge_delete_permissive_policies;
449 : : List *merge_delete_restrictive_policies;
450 : : List *merge_insert_permissive_policies;
451 : : List *merge_insert_restrictive_policies;
452 : 116 : List *merge_select_permissive_policies = NIL;
453 : 116 : List *merge_select_restrictive_policies = NIL;
454 : :
455 : : /*
456 : : * Fetch the UPDATE policies and set them up to execute on the
457 : : * existing target row before doing UPDATE.
458 : : */
459 : 116 : get_policies_for_relation(rel, CMD_UPDATE, user_id,
460 : : &merge_update_permissive_policies,
461 : : &merge_update_restrictive_policies);
462 : :
463 : : /*
464 : : * WCO_RLS_MERGE_UPDATE_CHECK is used to check UPDATE USING quals on
465 : : * the existing target row.
466 : : */
467 : 116 : add_with_check_options(rel, rt_index,
468 : : WCO_RLS_MERGE_UPDATE_CHECK,
469 : : merge_update_permissive_policies,
470 : : merge_update_restrictive_policies,
471 : : withCheckOptions,
472 : : hasSubLinks,
473 : : true);
474 : :
475 : : /* Enforce the WITH CHECK clauses of the UPDATE policies */
476 : 116 : add_with_check_options(rel, rt_index,
477 : : WCO_RLS_UPDATE_CHECK,
478 : : merge_update_permissive_policies,
479 : : merge_update_restrictive_policies,
480 : : withCheckOptions,
481 : : hasSubLinks,
482 : : false);
483 : :
484 : : /*
485 : : * Add ALL/SELECT policies as WCO_RLS_UPDATE_CHECK WCOs, to ensure
486 : : * that the updated row is visible when executing an UPDATE action, if
487 : : * SELECT rights are required for this relation.
488 : : */
489 [ + - ]: 116 : if (perminfo->requiredPerms & ACL_SELECT)
490 : : {
491 : 116 : get_policies_for_relation(rel, CMD_SELECT, user_id,
492 : : &merge_select_permissive_policies,
493 : : &merge_select_restrictive_policies);
494 : 116 : add_with_check_options(rel, rt_index,
495 : : WCO_RLS_UPDATE_CHECK,
496 : : merge_select_permissive_policies,
497 : : merge_select_restrictive_policies,
498 : : withCheckOptions,
499 : : hasSubLinks,
500 : : true);
501 : : }
502 : :
503 : : /*
504 : : * Fetch the DELETE policies and set them up to execute on the
505 : : * existing target row before doing DELETE.
506 : : */
507 : 116 : get_policies_for_relation(rel, CMD_DELETE, user_id,
508 : : &merge_delete_permissive_policies,
509 : : &merge_delete_restrictive_policies);
510 : :
511 : : /*
512 : : * WCO_RLS_MERGE_DELETE_CHECK is used to check DELETE USING quals on
513 : : * the existing target row.
514 : : */
515 : 116 : add_with_check_options(rel, rt_index,
516 : : WCO_RLS_MERGE_DELETE_CHECK,
517 : : merge_delete_permissive_policies,
518 : : merge_delete_restrictive_policies,
519 : : withCheckOptions,
520 : : hasSubLinks,
521 : : true);
522 : :
523 : : /*
524 : : * No special handling is required for INSERT policies. They will be
525 : : * checked and enforced during ExecInsert(). But we must add them to
526 : : * withCheckOptions.
527 : : */
528 : 116 : get_policies_for_relation(rel, CMD_INSERT, user_id,
529 : : &merge_insert_permissive_policies,
530 : : &merge_insert_restrictive_policies);
531 : :
532 : 116 : add_with_check_options(rel, rt_index,
533 : : WCO_RLS_INSERT_CHECK,
534 : : merge_insert_permissive_policies,
535 : : merge_insert_restrictive_policies,
536 : : withCheckOptions,
537 : : hasSubLinks,
538 : : false);
539 : :
540 : : /*
541 : : * Add ALL/SELECT policies as WCO_RLS_INSERT_CHECK WCOs, to ensure
542 : : * that the inserted row is visible when executing an INSERT action,
543 : : * if RETURNING is specified and SELECT rights are required for this
544 : : * relation.
545 : : */
546 [ + - + + ]: 116 : if (perminfo->requiredPerms & ACL_SELECT && root->returningList)
547 : 24 : add_with_check_options(rel, rt_index,
548 : : WCO_RLS_INSERT_CHECK,
549 : : merge_select_permissive_policies,
550 : : merge_select_restrictive_policies,
551 : : withCheckOptions,
552 : : hasSubLinks,
553 : : true);
554 : : }
555 : :
556 : 2243 : table_close(rel, NoLock);
557 : :
558 : : /*
559 : : * Copy checkAsUser to the row security quals and WithCheckOption checks,
560 : : * in case they contain any subqueries referring to other relations.
561 : : */
562 : 2243 : setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser);
563 : 2243 : setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser);
564 : :
565 : : /*
566 : : * Mark this query as having row security, so plancache can invalidate it
567 : : * when necessary (eg: role changes)
568 : : */
569 : 2243 : *hasRowSecurity = true;
570 : : }
571 : :
572 : : /*
573 : : * get_policies_for_relation
574 : : *
575 : : * Returns lists of permissive and restrictive policies to be applied to the
576 : : * specified relation, based on the command type and role.
577 : : *
578 : : * This includes any policies added by extensions.
579 : : */
580 : : static void
581 : 3655 : get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
582 : : List **permissive_policies,
583 : : List **restrictive_policies)
584 : : {
585 : : ListCell *item;
586 : :
587 : 3655 : *permissive_policies = NIL;
588 : 3655 : *restrictive_policies = NIL;
589 : :
590 : : /* First find all internal policies for the relation. */
591 [ + + + + : 12778 : foreach(item, relation->rd_rsdesc->policies)
+ + ]
592 : : {
593 : 9123 : bool cmd_matches = false;
594 : 9123 : RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
595 : :
596 : : /* Always add ALL policies, if they exist. */
597 [ + + ]: 9123 : if (policy->polcmd == '*')
598 : 3541 : cmd_matches = true;
599 : : else
600 : : {
601 : : /* Check whether the policy applies to the specified command type */
602 [ + + + + : 5582 : switch (cmd)
+ - ]
603 : : {
604 : 2690 : case CMD_SELECT:
605 [ + + ]: 2690 : if (policy->polcmd == ACL_SELECT_CHR)
606 : 890 : cmd_matches = true;
607 : 2690 : break;
608 : 952 : case CMD_INSERT:
609 [ + + ]: 952 : if (policy->polcmd == ACL_INSERT_CHR)
610 : 260 : cmd_matches = true;
611 : 952 : break;
612 : 988 : case CMD_UPDATE:
613 [ + + ]: 988 : if (policy->polcmd == ACL_UPDATE_CHR)
614 : 312 : cmd_matches = true;
615 : 988 : break;
616 : 552 : case CMD_DELETE:
617 [ + + ]: 552 : if (policy->polcmd == ACL_DELETE_CHR)
618 : 140 : cmd_matches = true;
619 : 552 : break;
620 : 400 : case CMD_MERGE:
621 : :
622 : : /*
623 : : * We do not support a separate policy for MERGE command.
624 : : * Instead it derives from the policies defined for other
625 : : * commands.
626 : : */
627 : 400 : break;
628 : 0 : default:
629 [ # # ]: 0 : elog(ERROR, "unrecognized policy command type %d",
630 : : (int) cmd);
631 : : break;
632 : : }
633 : : }
634 : :
635 : : /*
636 : : * Add this policy to the relevant list of policies if it applies to
637 : : * the specified role.
638 : : */
639 [ + + + + ]: 9123 : if (cmd_matches && check_role_for_policy(policy->roles, user_id))
640 : : {
641 [ + + ]: 3859 : if (policy->permissive)
642 : 3606 : *permissive_policies = lappend(*permissive_policies, policy);
643 : : else
644 : 253 : *restrictive_policies = lappend(*restrictive_policies, policy);
645 : : }
646 : : }
647 : :
648 : : /*
649 : : * We sort restrictive policies by name so that any WCOs they generate are
650 : : * checked in a well-defined order.
651 : : */
652 : 3655 : sort_policies_by_name(*restrictive_policies);
653 : :
654 : : /*
655 : : * Then add any permissive or restrictive policies defined by extensions.
656 : : * These are simply appended to the lists of internal policies, if they
657 : : * apply to the specified role.
658 : : */
659 [ + + ]: 3655 : if (row_security_policy_hook_restrictive)
660 : : {
661 : : List *hook_policies =
662 : 30 : (*row_security_policy_hook_restrictive) (cmd, relation);
663 : :
664 : : /*
665 : : * As with built-in restrictive policies, we sort any hook-provided
666 : : * restrictive policies by name also. Note that we also intentionally
667 : : * always check all built-in restrictive policies, in name order,
668 : : * before checking restrictive policies added by hooks, in name order.
669 : : */
670 : 30 : sort_policies_by_name(hook_policies);
671 : :
672 [ + + + + : 51 : foreach(item, hook_policies)
+ + ]
673 : : {
674 : 21 : RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
675 : :
676 [ + - ]: 21 : if (check_role_for_policy(policy->roles, user_id))
677 : 21 : *restrictive_policies = lappend(*restrictive_policies, policy);
678 : : }
679 : : }
680 : :
681 [ + + ]: 3655 : if (row_security_policy_hook_permissive)
682 : : {
683 : : List *hook_policies =
684 : 30 : (*row_security_policy_hook_permissive) (cmd, relation);
685 : :
686 [ + + + + : 50 : foreach(item, hook_policies)
+ + ]
687 : : {
688 : 20 : RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
689 : :
690 [ + - ]: 20 : if (check_role_for_policy(policy->roles, user_id))
691 : 20 : *permissive_policies = lappend(*permissive_policies, policy);
692 : : }
693 : : }
694 : 3655 : }
695 : :
696 : : /*
697 : : * sort_policies_by_name
698 : : *
699 : : * This is only used for restrictive policies, ensuring that any
700 : : * WithCheckOptions they generate are applied in a well-defined order.
701 : : * This is not necessary for permissive policies, since they are all combined
702 : : * together using OR into a single WithCheckOption check.
703 : : */
704 : : static void
705 : 3685 : sort_policies_by_name(List *policies)
706 : : {
707 : 3685 : list_sort(policies, row_security_policy_cmp);
708 : 3685 : }
709 : :
710 : : /*
711 : : * list_sort comparator to sort RowSecurityPolicy entries by name
712 : : */
713 : : static int
714 : 52 : row_security_policy_cmp(const ListCell *a, const ListCell *b)
715 : : {
716 : 52 : const RowSecurityPolicy *pa = (const RowSecurityPolicy *) lfirst(a);
717 : 52 : const RowSecurityPolicy *pb = (const RowSecurityPolicy *) lfirst(b);
718 : :
719 : : /* Guard against NULL policy names from extensions */
720 [ - + ]: 52 : if (pa->policy_name == NULL)
721 : 0 : return pb->policy_name == NULL ? 0 : 1;
722 [ - + ]: 52 : if (pb->policy_name == NULL)
723 : 0 : return -1;
724 : :
725 : 52 : return strcmp(pa->policy_name, pb->policy_name);
726 : : }
727 : :
728 : : /*
729 : : * add_security_quals
730 : : *
731 : : * Add security quals to enforce the specified RLS policies, restricting
732 : : * access to existing data in a table. If there are no policies controlling
733 : : * access to the table, then all access is prohibited --- i.e., an implicit
734 : : * default-deny policy is used.
735 : : *
736 : : * New security quals are added to securityQuals, and hasSubLinks is set to
737 : : * true if any of the quals added contain sublink subqueries.
738 : : */
739 : : static void
740 : 2257 : add_security_quals(int rt_index,
741 : : List *permissive_policies,
742 : : List *restrictive_policies,
743 : : List **securityQuals,
744 : : bool *hasSubLinks)
745 : : {
746 : : ListCell *item;
747 : 2257 : List *permissive_quals = NIL;
748 : : Expr *rowsec_expr;
749 : :
750 : : /*
751 : : * First collect up the permissive quals. If we do not find any
752 : : * permissive policies then no rows are visible (this is handled below).
753 : : */
754 [ + + + + : 4594 : foreach(item, permissive_policies)
+ + ]
755 : : {
756 : 2337 : RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
757 : :
758 [ + - ]: 2337 : if (policy->qual != NULL)
759 : : {
760 : 2337 : permissive_quals = lappend(permissive_quals,
761 : 2337 : copyObject(policy->qual));
762 : 2337 : *hasSubLinks |= policy->hassublinks;
763 : : }
764 : : }
765 : :
766 : : /*
767 : : * We must have permissive quals, always, or no rows are visible.
768 : : *
769 : : * If we do not, then we simply return a single 'false' qual which results
770 : : * in no rows being visible.
771 : : */
772 [ + + ]: 2257 : if (permissive_quals != NIL)
773 : : {
774 : : /*
775 : : * We now know that permissive policies exist, so we can now add
776 : : * security quals based on the USING clauses from the restrictive
777 : : * policies. Since these need to be combined together using AND, we
778 : : * can just add them one at a time.
779 : : */
780 [ + + + + : 2418 : foreach(item, restrictive_policies)
+ + ]
781 : : {
782 : 197 : RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
783 : : Expr *qual;
784 : :
785 [ + - ]: 197 : if (policy->qual != NULL)
786 : : {
787 : 197 : qual = copyObject(policy->qual);
788 : 197 : ChangeVarNodes((Node *) qual, 1, rt_index, 0);
789 : :
790 : 197 : *securityQuals = list_append_unique(*securityQuals, qual);
791 : 197 : *hasSubLinks |= policy->hassublinks;
792 : : }
793 : : }
794 : :
795 : : /*
796 : : * Then add a single security qual combining together the USING
797 : : * clauses from all the permissive policies using OR.
798 : : */
799 [ + + ]: 2221 : if (list_length(permissive_quals) == 1)
800 : 2137 : rowsec_expr = (Expr *) linitial(permissive_quals);
801 : : else
802 : 84 : rowsec_expr = makeBoolExpr(OR_EXPR, permissive_quals, -1);
803 : :
804 : 2221 : ChangeVarNodes((Node *) rowsec_expr, 1, rt_index, 0);
805 : 2221 : *securityQuals = list_append_unique(*securityQuals, rowsec_expr);
806 : : }
807 : : else
808 : :
809 : : /*
810 : : * A permissive policy must exist for rows to be visible at all.
811 : : * Therefore, if there were no permissive policies found, return a
812 : : * single always-false clause.
813 : : */
814 : 36 : *securityQuals = lappend(*securityQuals,
815 : 36 : makeConst(BOOLOID, -1, InvalidOid,
816 : : sizeof(bool), BoolGetDatum(false),
817 : : false, true));
818 : 2257 : }
819 : :
820 : : /*
821 : : * add_with_check_options
822 : : *
823 : : * Add WithCheckOptions of the specified kind to check that new records
824 : : * added by an INSERT or UPDATE are consistent with the specified RLS
825 : : * policies. Normally new data must satisfy the WITH CHECK clauses from the
826 : : * policies. If a policy has no explicit WITH CHECK clause, its USING clause
827 : : * is used instead. In the special case of a SELECT or UPDATE arising from an
828 : : * INSERT ... ON CONFLICT DO SELECT/UPDATE, existing records are first checked
829 : : * using a WCO_RLS_CONFLICT_CHECK WithCheckOption, which always uses the USING
830 : : * clauses from RLS policies.
831 : : *
832 : : * New WCOs are added to withCheckOptions, and hasSubLinks is set to true if
833 : : * any of the check clauses added contain sublink subqueries.
834 : : */
835 : : static void
836 : 1774 : add_with_check_options(Relation rel,
837 : : int rt_index,
838 : : WCOKind kind,
839 : : List *permissive_policies,
840 : : List *restrictive_policies,
841 : : List **withCheckOptions,
842 : : bool *hasSubLinks,
843 : : bool force_using)
844 : : {
845 : : ListCell *item;
846 : 1774 : List *permissive_quals = NIL;
847 : :
848 : : #define QUAL_FOR_WCO(policy) \
849 : : ( !force_using && \
850 : : (policy)->with_check_qual != NULL ? \
851 : : (policy)->with_check_qual : (policy)->qual )
852 : :
853 : : /*
854 : : * First collect up the permissive policy clauses, similar to
855 : : * add_security_quals.
856 : : */
857 [ + + + + : 3539 : foreach(item, permissive_policies)
+ + ]
858 : : {
859 : 1765 : RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
860 [ + + + + ]: 1765 : Expr *qual = QUAL_FOR_WCO(policy);
861 : :
862 [ + - ]: 1765 : if (qual != NULL)
863 : : {
864 : 1765 : permissive_quals = lappend(permissive_quals, copyObject(qual));
865 : 1765 : *hasSubLinks |= policy->hassublinks;
866 : : }
867 : : }
868 : :
869 : : /*
870 : : * There must be at least one permissive qual found or no rows are allowed
871 : : * to be added. This is the same as in add_security_quals.
872 : : *
873 : : * If there are no permissive_quals then we fall through and return a
874 : : * single 'false' WCO, preventing all new rows.
875 : : */
876 [ + + ]: 1774 : if (permissive_quals != NIL)
877 : : {
878 : : /*
879 : : * Add a single WithCheckOption for all the permissive policy clauses,
880 : : * combining them together using OR. This check has no policy name,
881 : : * since if the check fails it means that no policy granted permission
882 : : * to perform the update, rather than any particular policy being
883 : : * violated.
884 : : */
885 : : WithCheckOption *wco;
886 : :
887 : 1738 : wco = makeNode(WithCheckOption);
888 : 1738 : wco->kind = kind;
889 : 1738 : wco->relname = pstrdup(RelationGetRelationName(rel));
890 : 1738 : wco->polname = NULL;
891 : 1738 : wco->cascaded = false;
892 : :
893 [ + + ]: 1738 : if (list_length(permissive_quals) == 1)
894 : 1711 : wco->qual = (Node *) linitial(permissive_quals);
895 : : else
896 : 27 : wco->qual = (Node *) makeBoolExpr(OR_EXPR, permissive_quals, -1);
897 : :
898 : 1738 : ChangeVarNodes(wco->qual, 1, rt_index, 0);
899 : :
900 : 1738 : *withCheckOptions = list_append_unique(*withCheckOptions, wco);
901 : :
902 : : /*
903 : : * Now add WithCheckOptions for each of the restrictive policy clauses
904 : : * (which will be combined together using AND). We use a separate
905 : : * WithCheckOption for each restrictive policy to allow the policy
906 : : * name to be included in error reports if the policy is violated.
907 : : */
908 [ + + + + : 1831 : foreach(item, restrictive_policies)
+ + ]
909 : : {
910 : 93 : RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
911 [ + - + + ]: 93 : Expr *qual = QUAL_FOR_WCO(policy);
912 : :
913 [ + - ]: 93 : if (qual != NULL)
914 : : {
915 : 93 : qual = copyObject(qual);
916 : 93 : ChangeVarNodes((Node *) qual, 1, rt_index, 0);
917 : :
918 : 93 : wco = makeNode(WithCheckOption);
919 : 93 : wco->kind = kind;
920 : 93 : wco->relname = pstrdup(RelationGetRelationName(rel));
921 : 93 : wco->polname = pstrdup(policy->policy_name);
922 : 93 : wco->qual = (Node *) qual;
923 : 93 : wco->cascaded = false;
924 : :
925 : 93 : *withCheckOptions = list_append_unique(*withCheckOptions, wco);
926 : 93 : *hasSubLinks |= policy->hassublinks;
927 : : }
928 : : }
929 : : }
930 : : else
931 : : {
932 : : /*
933 : : * If there were no policy clauses to check new data, add a single
934 : : * always-false WCO (a default-deny policy).
935 : : */
936 : : WithCheckOption *wco;
937 : :
938 : 36 : wco = makeNode(WithCheckOption);
939 : 36 : wco->kind = kind;
940 : 36 : wco->relname = pstrdup(RelationGetRelationName(rel));
941 : 36 : wco->polname = NULL;
942 : 36 : wco->qual = (Node *) makeConst(BOOLOID, -1, InvalidOid,
943 : : sizeof(bool), BoolGetDatum(false),
944 : : false, true);
945 : 36 : wco->cascaded = false;
946 : :
947 : 36 : *withCheckOptions = lappend(*withCheckOptions, wco);
948 : : }
949 : 1774 : }
950 : :
951 : : /*
952 : : * check_role_for_policy -
953 : : * determines if the policy should be applied for the current role
954 : : */
955 : : static bool
956 : 5184 : check_role_for_policy(ArrayType *policy_roles, Oid user_id)
957 : : {
958 : : int i;
959 [ - + ]: 5184 : Oid *roles = (Oid *) ARR_DATA_PTR(policy_roles);
960 : :
961 : : /* Quick fall-thru for policies applied to all roles */
962 [ + + ]: 5184 : if (roles[0] == ACL_ID_PUBLIC)
963 : 3423 : return true;
964 : :
965 [ + + ]: 3045 : for (i = 0; i < ARR_DIMS(policy_roles)[0]; i++)
966 : : {
967 [ + + ]: 1761 : if (has_privs_of_role(user_id, roles[i]))
968 : 477 : return true;
969 : : }
970 : :
971 : 1284 : return false;
972 : : }
|