PostgreSQL Source Code  git master
rowsecurity.h File Reference
#include "nodes/parsenodes.h"
#include "utils/array.h"
#include "utils/relcache.h"
Include dependency graph for rowsecurity.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  RowSecurityPolicy
 
struct  RowSecurityDesc
 

Typedefs

typedef struct RowSecurityPolicy RowSecurityPolicy
 
typedef struct RowSecurityDesc RowSecurityDesc
 
typedef List *(* row_security_policy_hook_type) (CmdType cmdtype, Relation relation)
 

Functions

void get_row_security_policies (Query *root, RangeTblEntry *rte, int rt_index, List **securityQuals, List **withCheckOptions, bool *hasRowSecurity, bool *hasSubLinks)
 

Variables

PGDLLIMPORT row_security_policy_hook_type row_security_policy_hook_permissive
 
PGDLLIMPORT row_security_policy_hook_type row_security_policy_hook_restrictive
 

Typedef Documentation

◆ row_security_policy_hook_type

typedef List*(* row_security_policy_hook_type) (CmdType cmdtype, Relation relation)

Definition at line 37 of file rowsecurity.h.

◆ RowSecurityDesc

◆ RowSecurityPolicy

Function Documentation

◆ get_row_security_policies()

void get_row_security_policies ( Query root,
RangeTblEntry rte,
int  rt_index,
List **  securityQuals,
List **  withCheckOptions,
bool hasRowSecurity,
bool hasSubLinks 
)

Definition at line 109 of file rowsecurity.c.

112 {
113  Oid user_id;
114  int rls_status;
115  Relation rel;
116  CmdType commandType;
117  List *permissive_policies;
118  List *restrictive_policies;
119  RTEPermissionInfo *perminfo;
120 
121  /* Defaults for the return values */
122  *securityQuals = NIL;
123  *withCheckOptions = NIL;
124  *hasRowSecurity = false;
125  *hasSubLinks = false;
126 
127  Assert(rte->rtekind == RTE_RELATION);
128 
129  /* If this is not a normal relation, just return immediately */
130  if (rte->relkind != RELKIND_RELATION &&
131  rte->relkind != RELKIND_PARTITIONED_TABLE)
132  return;
133 
134  perminfo = getRTEPermissionInfo(root->rteperminfos, rte);
135 
136  /* Switch to checkAsUser if it's set */
137  user_id = OidIsValid(perminfo->checkAsUser) ?
138  perminfo->checkAsUser : GetUserId();
139 
140  /* Determine the state of RLS for this, pass checkAsUser explicitly */
141  rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false);
142 
143  /* If there is no RLS on this table at all, nothing to do */
144  if (rls_status == RLS_NONE)
145  return;
146 
147  /*
148  * RLS_NONE_ENV means we are not doing any RLS now, but that may change
149  * with changes to the environment, so we mark it as hasRowSecurity to
150  * force a re-plan when the environment changes.
151  */
152  if (rls_status == RLS_NONE_ENV)
153  {
154  /*
155  * Indicate that this query may involve RLS and must therefore be
156  * replanned if the environment changes (GUCs, role), but we are not
157  * adding anything here.
158  */
159  *hasRowSecurity = true;
160 
161  return;
162  }
163 
164  /*
165  * RLS is enabled for this relation.
166  *
167  * Get the security policies that should be applied, based on the command
168  * type. Note that if this isn't the target relation, we actually want
169  * the relation's SELECT policies, regardless of the query command type,
170  * for example in UPDATE t1 ... FROM t2 we need to apply t1's UPDATE
171  * policies and t2's SELECT policies.
172  */
173  rel = table_open(rte->relid, NoLock);
174 
175  commandType = rt_index == root->resultRelation ?
176  root->commandType : CMD_SELECT;
177 
178  /*
179  * In some cases, we need to apply USING policies (which control the
180  * visibility of records) associated with multiple command types (see
181  * specific cases below).
182  *
183  * When considering the order in which to apply these USING policies, we
184  * prefer to apply higher privileged policies, those which allow the user
185  * to lock records (UPDATE and DELETE), first, followed by policies which
186  * don't (SELECT).
187  *
188  * Note that the optimizer is free to push down and reorder quals which
189  * use leakproof functions.
190  *
191  * In all cases, if there are no policy clauses allowing access to rows in
192  * the table for the specific type of operation, then a single
193  * always-false clause (a default-deny policy) will be added (see
194  * add_security_quals).
195  */
196 
197  /*
198  * For a SELECT, if UPDATE privileges are required (eg: the user has
199  * specified FOR [KEY] UPDATE/SHARE), then add the UPDATE USING quals
200  * first.
201  *
202  * This way, we filter out any records from the SELECT FOR SHARE/UPDATE
203  * which the user does not have access to via the UPDATE USING policies,
204  * similar to how we require normal UPDATE rights for these queries.
205  */
206  if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE)
207  {
208  List *update_permissive_policies;
209  List *update_restrictive_policies;
210 
212  &update_permissive_policies,
213  &update_restrictive_policies);
214 
215  add_security_quals(rt_index,
216  update_permissive_policies,
217  update_restrictive_policies,
218  securityQuals,
219  hasSubLinks);
220  }
221 
222  /*
223  * For SELECT, UPDATE and DELETE, add security quals to enforce the USING
224  * policies. These security quals control access to existing table rows.
225  * Restrictive policies are combined together using AND, and permissive
226  * policies are combined together using OR.
227  */
228 
229  get_policies_for_relation(rel, commandType, user_id, &permissive_policies,
230  &restrictive_policies);
231 
232  if (commandType == CMD_SELECT ||
233  commandType == CMD_UPDATE ||
234  commandType == CMD_DELETE)
235  add_security_quals(rt_index,
236  permissive_policies,
237  restrictive_policies,
238  securityQuals,
239  hasSubLinks);
240 
241  /*
242  * Similar to above, during an UPDATE, DELETE, or MERGE, if SELECT rights
243  * are also required (eg: when a RETURNING clause exists, or the user has
244  * provided a WHERE clause which involves columns from the relation), we
245  * collect up CMD_SELECT policies and add them via add_security_quals
246  * first.
247  *
248  * This way, we filter out any records which are not visible through an
249  * ALL or SELECT USING policy.
250  */
251  if ((commandType == CMD_UPDATE || commandType == CMD_DELETE ||
252  commandType == CMD_MERGE) &&
253  perminfo->requiredPerms & ACL_SELECT)
254  {
255  List *select_permissive_policies;
256  List *select_restrictive_policies;
257 
259  &select_permissive_policies,
260  &select_restrictive_policies);
261 
262  add_security_quals(rt_index,
263  select_permissive_policies,
264  select_restrictive_policies,
265  securityQuals,
266  hasSubLinks);
267  }
268 
269  /*
270  * For INSERT and UPDATE, add withCheckOptions to verify that any new
271  * records added are consistent with the security policies. This will use
272  * each policy's WITH CHECK clause, or its USING clause if no explicit
273  * WITH CHECK clause is defined.
274  */
275  if (commandType == CMD_INSERT || commandType == CMD_UPDATE)
276  {
277  /* This should be the target relation */
278  Assert(rt_index == root->resultRelation);
279 
280  add_with_check_options(rel, rt_index,
281  commandType == CMD_INSERT ?
283  permissive_policies,
284  restrictive_policies,
285  withCheckOptions,
286  hasSubLinks,
287  false);
288 
289  /*
290  * Get and add ALL/SELECT policies, if SELECT rights are required for
291  * this relation (eg: when RETURNING is used). These are added as WCO
292  * policies rather than security quals to ensure that an error is
293  * raised if a policy is violated; otherwise, we might end up silently
294  * dropping rows to be added.
295  */
296  if (perminfo->requiredPerms & ACL_SELECT)
297  {
298  List *select_permissive_policies = NIL;
299  List *select_restrictive_policies = NIL;
300 
302  &select_permissive_policies,
303  &select_restrictive_policies);
304  add_with_check_options(rel, rt_index,
305  commandType == CMD_INSERT ?
307  select_permissive_policies,
308  select_restrictive_policies,
309  withCheckOptions,
310  hasSubLinks,
311  true);
312  }
313 
314  /*
315  * For INSERT ... ON CONFLICT DO UPDATE we need additional policy
316  * checks for the UPDATE which may be applied to the same RTE.
317  */
318  if (commandType == CMD_INSERT &&
319  root->onConflict && root->onConflict->action == ONCONFLICT_UPDATE)
320  {
321  List *conflict_permissive_policies;
322  List *conflict_restrictive_policies;
323  List *conflict_select_permissive_policies = NIL;
324  List *conflict_select_restrictive_policies = NIL;
325 
326  /* Get the policies that apply to the auxiliary UPDATE */
328  &conflict_permissive_policies,
329  &conflict_restrictive_policies);
330 
331  /*
332  * Enforce the USING clauses of the UPDATE policies using WCOs
333  * rather than security quals. This ensures that an error is
334  * raised if the conflicting row cannot be updated due to RLS,
335  * rather than the change being silently dropped.
336  */
337  add_with_check_options(rel, rt_index,
339  conflict_permissive_policies,
340  conflict_restrictive_policies,
341  withCheckOptions,
342  hasSubLinks,
343  true);
344 
345  /*
346  * Get and add ALL/SELECT policies, as WCO_RLS_CONFLICT_CHECK WCOs
347  * to ensure they are considered when taking the UPDATE path of an
348  * INSERT .. ON CONFLICT DO UPDATE, if SELECT rights are required
349  * for this relation, also as WCO policies, again, to avoid
350  * silently dropping data. See above.
351  */
352  if (perminfo->requiredPerms & ACL_SELECT)
353  {
355  &conflict_select_permissive_policies,
356  &conflict_select_restrictive_policies);
357  add_with_check_options(rel, rt_index,
359  conflict_select_permissive_policies,
360  conflict_select_restrictive_policies,
361  withCheckOptions,
362  hasSubLinks,
363  true);
364  }
365 
366  /* Enforce the WITH CHECK clauses of the UPDATE policies */
367  add_with_check_options(rel, rt_index,
369  conflict_permissive_policies,
370  conflict_restrictive_policies,
371  withCheckOptions,
372  hasSubLinks,
373  false);
374 
375  /*
376  * Add ALL/SELECT policies as WCO_RLS_UPDATE_CHECK WCOs, to ensure
377  * that the final updated row is visible when taking the UPDATE
378  * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights
379  * are required for this relation.
380  */
381  if (perminfo->requiredPerms & ACL_SELECT)
382  add_with_check_options(rel, rt_index,
384  conflict_select_permissive_policies,
385  conflict_select_restrictive_policies,
386  withCheckOptions,
387  hasSubLinks,
388  true);
389  }
390  }
391 
392  /*
393  * FOR MERGE, we fetch policies for UPDATE, DELETE and INSERT (and ALL)
394  * and set them up so that we can enforce the appropriate policy depending
395  * on the final action we take.
396  *
397  * We already fetched the SELECT policies above, to check existing rows,
398  * but we must also check that new rows created by UPDATE actions are
399  * visible, if SELECT rights are required for this relation. We don't do
400  * this for INSERT actions, since an INSERT command would only do this
401  * check if it had a RETURNING list, and MERGE does not support RETURNING.
402  *
403  * We don't push the UPDATE/DELETE USING quals to the RTE because we don't
404  * really want to apply them while scanning the relation since we don't
405  * know whether we will be doing an UPDATE or a DELETE at the end. We
406  * apply the respective policy once we decide the final action on the
407  * target tuple.
408  *
409  * XXX We are setting up USING quals as WITH CHECK. If RLS prohibits
410  * UPDATE/DELETE on the target row, we shall throw an error instead of
411  * silently ignoring the row. This is different than how normal
412  * UPDATE/DELETE works and more in line with INSERT ON CONFLICT DO UPDATE
413  * handling.
414  */
415  if (commandType == CMD_MERGE)
416  {
417  List *merge_update_permissive_policies;
418  List *merge_update_restrictive_policies;
419  List *merge_delete_permissive_policies;
420  List *merge_delete_restrictive_policies;
421  List *merge_insert_permissive_policies;
422  List *merge_insert_restrictive_policies;
423 
424  /*
425  * Fetch the UPDATE policies and set them up to execute on the
426  * existing target row before doing UPDATE.
427  */
429  &merge_update_permissive_policies,
430  &merge_update_restrictive_policies);
431 
432  /*
433  * WCO_RLS_MERGE_UPDATE_CHECK is used to check UPDATE USING quals on
434  * the existing target row.
435  */
436  add_with_check_options(rel, rt_index,
438  merge_update_permissive_policies,
439  merge_update_restrictive_policies,
440  withCheckOptions,
441  hasSubLinks,
442  true);
443 
444  /* Enforce the WITH CHECK clauses of the UPDATE policies */
445  add_with_check_options(rel, rt_index,
447  merge_update_permissive_policies,
448  merge_update_restrictive_policies,
449  withCheckOptions,
450  hasSubLinks,
451  false);
452 
453  /*
454  * Add ALL/SELECT policies as WCO_RLS_UPDATE_CHECK WCOs, to ensure
455  * that the updated row is visible when executing an UPDATE action, if
456  * SELECT rights are required for this relation.
457  */
458  if (perminfo->requiredPerms & ACL_SELECT)
459  {
460  List *merge_select_permissive_policies;
461  List *merge_select_restrictive_policies;
462 
464  &merge_select_permissive_policies,
465  &merge_select_restrictive_policies);
466  add_with_check_options(rel, rt_index,
468  merge_select_permissive_policies,
469  merge_select_restrictive_policies,
470  withCheckOptions,
471  hasSubLinks,
472  true);
473  }
474 
475  /*
476  * Fetch the DELETE policies and set them up to execute on the
477  * existing target row before doing DELETE.
478  */
480  &merge_delete_permissive_policies,
481  &merge_delete_restrictive_policies);
482 
483  /*
484  * WCO_RLS_MERGE_DELETE_CHECK is used to check DELETE USING quals on
485  * the existing target row.
486  */
487  add_with_check_options(rel, rt_index,
489  merge_delete_permissive_policies,
490  merge_delete_restrictive_policies,
491  withCheckOptions,
492  hasSubLinks,
493  true);
494 
495  /*
496  * No special handling is required for INSERT policies. They will be
497  * checked and enforced during ExecInsert(). But we must add them to
498  * withCheckOptions.
499  */
501  &merge_insert_permissive_policies,
502  &merge_insert_restrictive_policies);
503 
504  add_with_check_options(rel, rt_index,
506  merge_insert_permissive_policies,
507  merge_insert_restrictive_policies,
508  withCheckOptions,
509  hasSubLinks,
510  false);
511  }
512 
513  table_close(rel, NoLock);
514 
515  /*
516  * Copy checkAsUser to the row security quals and WithCheckOption checks,
517  * in case they contain any subqueries referring to other relations.
518  */
519  setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser);
520  setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser);
521 
522  /*
523  * Mark this query as having row security, so plancache can invalidate it
524  * when necessary (eg: role changes)
525  */
526  *hasRowSecurity = true;
527 }
#define OidIsValid(objectId)
Definition: c.h:764
Assert(fmt[strlen(fmt) - 1] !='\n')
#define NoLock
Definition: lockdefs.h:34
Oid GetUserId(void)
Definition: miscinit.c:509
@ ONCONFLICT_UPDATE
Definition: nodes.h:430
CmdType
Definition: nodes.h:274
@ CMD_MERGE
Definition: nodes.h:280
@ CMD_INSERT
Definition: nodes.h:278
@ CMD_DELETE
Definition: nodes.h:279
@ CMD_UPDATE
Definition: nodes.h:277
@ CMD_SELECT
Definition: nodes.h:276
RTEPermissionInfo * getRTEPermissionInfo(List *rteperminfos, RangeTblEntry *rte)
@ WCO_RLS_MERGE_UPDATE_CHECK
Definition: parsenodes.h:1310
@ WCO_RLS_CONFLICT_CHECK
Definition: parsenodes.h:1309
@ WCO_RLS_INSERT_CHECK
Definition: parsenodes.h:1307
@ WCO_RLS_UPDATE_CHECK
Definition: parsenodes.h:1308
@ WCO_RLS_MERGE_DELETE_CHECK
Definition: parsenodes.h:1311
#define ACL_UPDATE
Definition: parsenodes.h:78
@ RTE_RELATION
Definition: parsenodes.h:1006
#define ACL_SELECT
Definition: parsenodes.h:77
#define NIL
Definition: pg_list.h:68
unsigned int Oid
Definition: postgres_ext.h:31
void setRuleCheckAsUser(Node *node, Oid userid)
int check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
Definition: rls.c:52
@ RLS_NONE
Definition: rls.h:43
@ RLS_NONE_ENV
Definition: rls.h:44
static void add_with_check_options(Relation rel, int rt_index, WCOKind kind, List *permissive_policies, List *restrictive_policies, List **withCheckOptions, bool *hasSubLinks, bool force_using)
Definition: rowsecurity.c:793
static void get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id, List **permissive_policies, List **restrictive_policies)
Definition: rowsecurity.c:538
static void add_security_quals(int rt_index, List *permissive_policies, List *restrictive_policies, List **securityQuals, bool *hasSubLinks)
Definition: rowsecurity.c:697
Definition: pg_list.h:54
Definition: nodes.h:129
OnConflictAction action
Definition: primnodes.h:2056
OnConflictExpr * onConflict
Definition: parsenodes.h:186
CmdType commandType
Definition: parsenodes.h:120
AclMode requiredPerms
Definition: parsenodes.h:1238
RTEKind rtekind
Definition: parsenodes.h:1025
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40

References ACL_SELECT, ACL_UPDATE, OnConflictExpr::action, add_security_quals(), add_with_check_options(), Assert(), check_enable_rls(), RTEPermissionInfo::checkAsUser, CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_SELECT, CMD_UPDATE, Query::commandType, get_policies_for_relation(), getRTEPermissionInfo(), GetUserId(), NIL, NoLock, OidIsValid, Query::onConflict, ONCONFLICT_UPDATE, RangeTblEntry::relid, RangeTblEntry::relkind, RTEPermissionInfo::requiredPerms, RLS_NONE, RLS_NONE_ENV, RTE_RELATION, RangeTblEntry::rtekind, setRuleCheckAsUser(), table_close(), table_open(), WCO_RLS_CONFLICT_CHECK, WCO_RLS_INSERT_CHECK, WCO_RLS_MERGE_DELETE_CHECK, WCO_RLS_MERGE_UPDATE_CHECK, and WCO_RLS_UPDATE_CHECK.

Referenced by fireRIRrules().

Variable Documentation

◆ row_security_policy_hook_permissive

PGDLLIMPORT row_security_policy_hook_type row_security_policy_hook_permissive
extern

Definition at line 97 of file rowsecurity.c.

Referenced by _PG_init(), and get_policies_for_relation().

◆ row_security_policy_hook_restrictive

PGDLLIMPORT row_security_policy_hook_type row_security_policy_hook_restrictive
extern

Definition at line 98 of file rowsecurity.c.

Referenced by _PG_init(), and get_policies_for_relation().