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