Coverage Report

Created: 2026-08-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/rewrite/rowsecurity.c
Line
Count
Source
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
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 SELECT/UPDATE we need additional
305
     * policy checks for the SELECT/UPDATE which may be applied to the
306
     * same RTE.
307
     */
308
0
    if (commandType == CMD_INSERT && root->onConflict &&
309
0
      (root->onConflict->action == ONCONFLICT_UPDATE ||
310
0
       root->onConflict->action == ONCONFLICT_SELECT))
311
0
    {
312
0
      List     *conflict_permissive_policies = NIL;
313
0
      List     *conflict_restrictive_policies = NIL;
314
0
      List     *conflict_select_permissive_policies = NIL;
315
0
      List     *conflict_select_restrictive_policies = NIL;
316
317
0
      if (perminfo->requiredPerms & ACL_UPDATE)
318
0
      {
319
        /*
320
         * Get the policies that apply to the auxiliary UPDATE or
321
         * SELECT FOR UPDATE/SHARE.
322
         */
323
0
        get_policies_for_relation(rel, CMD_UPDATE, user_id,
324
0
                      &conflict_permissive_policies,
325
0
                      &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
0
        add_with_check_options(rel, rt_index,
334
0
                     WCO_RLS_CONFLICT_CHECK,
335
0
                     conflict_permissive_policies,
336
0
                     conflict_restrictive_policies,
337
0
                     withCheckOptions,
338
0
                     hasSubLinks,
339
0
                     true);
340
0
      }
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
0
      if (perminfo->requiredPerms & ACL_SELECT)
350
0
      {
351
0
        get_policies_for_relation(rel, CMD_SELECT, user_id,
352
0
                      &conflict_select_permissive_policies,
353
0
                      &conflict_select_restrictive_policies);
354
0
        add_with_check_options(rel, rt_index,
355
0
                     WCO_RLS_CONFLICT_CHECK,
356
0
                     conflict_select_permissive_policies,
357
0
                     conflict_select_restrictive_policies,
358
0
                     withCheckOptions,
359
0
                     hasSubLinks,
360
0
                     true);
361
0
      }
362
363
      /*
364
       * For INSERT .. ON CONFLICT DO UPDATE, add additional policies to
365
       * be checked when the auxiliary UPDATE is executed.
366
       */
367
0
      if (root->onConflict->action == ONCONFLICT_UPDATE)
368
0
      {
369
        /* Enforce the WITH CHECK clauses of the UPDATE policies */
370
0
        add_with_check_options(rel, rt_index,
371
0
                     WCO_RLS_UPDATE_CHECK,
372
0
                     conflict_permissive_policies,
373
0
                     conflict_restrictive_policies,
374
0
                     withCheckOptions,
375
0
                     hasSubLinks,
376
0
                     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
0
        if (perminfo->requiredPerms & ACL_SELECT)
385
0
          add_with_check_options(rel, rt_index,
386
0
                       WCO_RLS_UPDATE_CHECK,
387
0
                       conflict_select_permissive_policies,
388
0
                       conflict_select_restrictive_policies,
389
0
                       withCheckOptions,
390
0
                       hasSubLinks,
391
0
                       true);
392
0
      }
393
0
    }
394
0
  }
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
0
  if (root->forPortionOf != NULL && rt_index == root->resultRelation &&
404
0
    (commandType == CMD_UPDATE || commandType == CMD_DELETE))
405
0
  {
406
0
    List     *insert_permissive_policies;
407
0
    List     *insert_restrictive_policies;
408
409
0
    get_policies_for_relation(rel, CMD_INSERT, user_id,
410
0
                  &insert_permissive_policies,
411
0
                  &insert_restrictive_policies);
412
0
    add_with_check_options(rel, rt_index,
413
0
                 WCO_RLS_INSERT_CHECK,
414
0
                 insert_permissive_policies,
415
0
                 insert_restrictive_policies,
416
0
                 withCheckOptions,
417
0
                 hasSubLinks,
418
0
                 false);
419
0
  }
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
0
  if (commandType == CMD_MERGE)
445
0
  {
446
0
    List     *merge_update_permissive_policies;
447
0
    List     *merge_update_restrictive_policies;
448
0
    List     *merge_delete_permissive_policies;
449
0
    List     *merge_delete_restrictive_policies;
450
0
    List     *merge_insert_permissive_policies;
451
0
    List     *merge_insert_restrictive_policies;
452
0
    List     *merge_select_permissive_policies = NIL;
453
0
    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
0
    get_policies_for_relation(rel, CMD_UPDATE, user_id,
460
0
                  &merge_update_permissive_policies,
461
0
                  &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
0
    add_with_check_options(rel, rt_index,
468
0
                 WCO_RLS_MERGE_UPDATE_CHECK,
469
0
                 merge_update_permissive_policies,
470
0
                 merge_update_restrictive_policies,
471
0
                 withCheckOptions,
472
0
                 hasSubLinks,
473
0
                 true);
474
475
    /* Enforce the WITH CHECK clauses of the UPDATE policies */
476
0
    add_with_check_options(rel, rt_index,
477
0
                 WCO_RLS_UPDATE_CHECK,
478
0
                 merge_update_permissive_policies,
479
0
                 merge_update_restrictive_policies,
480
0
                 withCheckOptions,
481
0
                 hasSubLinks,
482
0
                 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
0
    if (perminfo->requiredPerms & ACL_SELECT)
490
0
    {
491
0
      get_policies_for_relation(rel, CMD_SELECT, user_id,
492
0
                    &merge_select_permissive_policies,
493
0
                    &merge_select_restrictive_policies);
494
0
      add_with_check_options(rel, rt_index,
495
0
                   WCO_RLS_UPDATE_CHECK,
496
0
                   merge_select_permissive_policies,
497
0
                   merge_select_restrictive_policies,
498
0
                   withCheckOptions,
499
0
                   hasSubLinks,
500
0
                   true);
501
0
    }
502
503
    /*
504
     * Fetch the DELETE policies and set them up to execute on the
505
     * existing target row before doing DELETE.
506
     */
507
0
    get_policies_for_relation(rel, CMD_DELETE, user_id,
508
0
                  &merge_delete_permissive_policies,
509
0
                  &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
0
    add_with_check_options(rel, rt_index,
516
0
                 WCO_RLS_MERGE_DELETE_CHECK,
517
0
                 merge_delete_permissive_policies,
518
0
                 merge_delete_restrictive_policies,
519
0
                 withCheckOptions,
520
0
                 hasSubLinks,
521
0
                 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
0
    get_policies_for_relation(rel, CMD_INSERT, user_id,
529
0
                  &merge_insert_permissive_policies,
530
0
                  &merge_insert_restrictive_policies);
531
532
0
    add_with_check_options(rel, rt_index,
533
0
                 WCO_RLS_INSERT_CHECK,
534
0
                 merge_insert_permissive_policies,
535
0
                 merge_insert_restrictive_policies,
536
0
                 withCheckOptions,
537
0
                 hasSubLinks,
538
0
                 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
0
    if (perminfo->requiredPerms & ACL_SELECT && root->returningList)
547
0
      add_with_check_options(rel, rt_index,
548
0
                   WCO_RLS_INSERT_CHECK,
549
0
                   merge_select_permissive_policies,
550
0
                   merge_select_restrictive_policies,
551
0
                   withCheckOptions,
552
0
                   hasSubLinks,
553
0
                   true);
554
0
  }
555
556
0
  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
0
  setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser);
563
0
  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
0
  *hasRowSecurity = true;
570
0
}
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
get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
582
              List **permissive_policies,
583
              List **restrictive_policies)
584
0
{
585
0
  ListCell   *item;
586
587
0
  *permissive_policies = NIL;
588
0
  *restrictive_policies = NIL;
589
590
  /* First find all internal policies for the relation. */
591
0
  foreach(item, relation->rd_rsdesc->policies)
592
0
  {
593
0
    bool    cmd_matches = false;
594
0
    RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
595
596
    /* Always add ALL policies, if they exist. */
597
0
    if (policy->polcmd == '*')
598
0
      cmd_matches = true;
599
0
    else
600
0
    {
601
      /* Check whether the policy applies to the specified command type */
602
0
      switch (cmd)
603
0
      {
604
0
        case CMD_SELECT:
605
0
          if (policy->polcmd == ACL_SELECT_CHR)
606
0
            cmd_matches = true;
607
0
          break;
608
0
        case CMD_INSERT:
609
0
          if (policy->polcmd == ACL_INSERT_CHR)
610
0
            cmd_matches = true;
611
0
          break;
612
0
        case CMD_UPDATE:
613
0
          if (policy->polcmd == ACL_UPDATE_CHR)
614
0
            cmd_matches = true;
615
0
          break;
616
0
        case CMD_DELETE:
617
0
          if (policy->polcmd == ACL_DELETE_CHR)
618
0
            cmd_matches = true;
619
0
          break;
620
0
        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
0
          break;
628
0
        default:
629
0
          elog(ERROR, "unrecognized policy command type %d",
630
0
             (int) cmd);
631
0
          break;
632
0
      }
633
0
    }
634
635
    /*
636
     * Add this policy to the relevant list of policies if it applies to
637
     * the specified role.
638
     */
639
0
    if (cmd_matches && check_role_for_policy(policy->roles, user_id))
640
0
    {
641
0
      if (policy->permissive)
642
0
        *permissive_policies = lappend(*permissive_policies, policy);
643
0
      else
644
0
        *restrictive_policies = lappend(*restrictive_policies, policy);
645
0
    }
646
0
  }
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
0
  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
0
  if (row_security_policy_hook_restrictive)
660
0
  {
661
0
    List     *hook_policies =
662
0
      (*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
0
    sort_policies_by_name(hook_policies);
671
672
0
    foreach(item, hook_policies)
673
0
    {
674
0
      RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
675
676
0
      if (check_role_for_policy(policy->roles, user_id))
677
0
        *restrictive_policies = lappend(*restrictive_policies, policy);
678
0
    }
679
0
  }
680
681
0
  if (row_security_policy_hook_permissive)
682
0
  {
683
0
    List     *hook_policies =
684
0
      (*row_security_policy_hook_permissive) (cmd, relation);
685
686
0
    foreach(item, hook_policies)
687
0
    {
688
0
      RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
689
690
0
      if (check_role_for_policy(policy->roles, user_id))
691
0
        *permissive_policies = lappend(*permissive_policies, policy);
692
0
    }
693
0
  }
694
0
}
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
sort_policies_by_name(List *policies)
706
0
{
707
0
  list_sort(policies, row_security_policy_cmp);
708
0
}
709
710
/*
711
 * list_sort comparator to sort RowSecurityPolicy entries by name
712
 */
713
static int
714
row_security_policy_cmp(const ListCell *a, const ListCell *b)
715
0
{
716
0
  const RowSecurityPolicy *pa = (const RowSecurityPolicy *) lfirst(a);
717
0
  const RowSecurityPolicy *pb = (const RowSecurityPolicy *) lfirst(b);
718
719
  /* Guard against NULL policy names from extensions */
720
0
  if (pa->policy_name == NULL)
721
0
    return pb->policy_name == NULL ? 0 : 1;
722
0
  if (pb->policy_name == NULL)
723
0
    return -1;
724
725
0
  return strcmp(pa->policy_name, pb->policy_name);
726
0
}
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
add_security_quals(int rt_index,
741
           List *permissive_policies,
742
           List *restrictive_policies,
743
           List **securityQuals,
744
           bool *hasSubLinks)
745
0
{
746
0
  ListCell   *item;
747
0
  List     *permissive_quals = NIL;
748
0
  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
0
  foreach(item, permissive_policies)
755
0
  {
756
0
    RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
757
758
0
    if (policy->qual != NULL)
759
0
    {
760
0
      permissive_quals = lappend(permissive_quals,
761
0
                     copyObject(policy->qual));
762
0
      *hasSubLinks |= policy->hassublinks;
763
0
    }
764
0
  }
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
0
  if (permissive_quals != NIL)
773
0
  {
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
0
    foreach(item, restrictive_policies)
781
0
    {
782
0
      RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
783
0
      Expr     *qual;
784
785
0
      if (policy->qual != NULL)
786
0
      {
787
0
        qual = copyObject(policy->qual);
788
0
        ChangeVarNodes((Node *) qual, 1, rt_index, 0);
789
790
0
        *securityQuals = list_append_unique(*securityQuals, qual);
791
0
        *hasSubLinks |= policy->hassublinks;
792
0
      }
793
0
    }
794
795
    /*
796
     * Then add a single security qual combining together the USING
797
     * clauses from all the permissive policies using OR.
798
     */
799
0
    if (list_length(permissive_quals) == 1)
800
0
      rowsec_expr = (Expr *) linitial(permissive_quals);
801
0
    else
802
0
      rowsec_expr = makeBoolExpr(OR_EXPR, permissive_quals, -1);
803
804
0
    ChangeVarNodes((Node *) rowsec_expr, 1, rt_index, 0);
805
0
    *securityQuals = list_append_unique(*securityQuals, rowsec_expr);
806
0
  }
807
0
  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
0
    *securityQuals = lappend(*securityQuals,
815
0
                 makeConst(BOOLOID, -1, InvalidOid,
816
0
                       sizeof(bool), BoolGetDatum(false),
817
0
                       false, true));
818
0
}
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
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
0
{
845
0
  ListCell   *item;
846
0
  List     *permissive_quals = NIL;
847
848
0
#define QUAL_FOR_WCO(policy) \
849
0
  ( !force_using && \
850
0
    (policy)->with_check_qual != NULL ? \
851
0
    (policy)->with_check_qual : (policy)->qual )
852
853
  /*
854
   * First collect up the permissive policy clauses, similar to
855
   * add_security_quals.
856
   */
857
0
  foreach(item, permissive_policies)
858
0
  {
859
0
    RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
860
0
    Expr     *qual = QUAL_FOR_WCO(policy);
861
862
0
    if (qual != NULL)
863
0
    {
864
0
      permissive_quals = lappend(permissive_quals, copyObject(qual));
865
0
      *hasSubLinks |= policy->hassublinks;
866
0
    }
867
0
  }
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
0
  if (permissive_quals != NIL)
877
0
  {
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
0
    WithCheckOption *wco;
886
887
0
    wco = makeNode(WithCheckOption);
888
0
    wco->kind = kind;
889
0
    wco->relname = pstrdup(RelationGetRelationName(rel));
890
0
    wco->polname = NULL;
891
0
    wco->cascaded = false;
892
893
0
    if (list_length(permissive_quals) == 1)
894
0
      wco->qual = (Node *) linitial(permissive_quals);
895
0
    else
896
0
      wco->qual = (Node *) makeBoolExpr(OR_EXPR, permissive_quals, -1);
897
898
0
    ChangeVarNodes(wco->qual, 1, rt_index, 0);
899
900
0
    *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
0
    foreach(item, restrictive_policies)
909
0
    {
910
0
      RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
911
0
      Expr     *qual = QUAL_FOR_WCO(policy);
912
913
0
      if (qual != NULL)
914
0
      {
915
0
        qual = copyObject(qual);
916
0
        ChangeVarNodes((Node *) qual, 1, rt_index, 0);
917
918
0
        wco = makeNode(WithCheckOption);
919
0
        wco->kind = kind;
920
0
        wco->relname = pstrdup(RelationGetRelationName(rel));
921
0
        wco->polname = pstrdup(policy->policy_name);
922
0
        wco->qual = (Node *) qual;
923
0
        wco->cascaded = false;
924
925
0
        *withCheckOptions = list_append_unique(*withCheckOptions, wco);
926
0
        *hasSubLinks |= policy->hassublinks;
927
0
      }
928
0
    }
929
0
  }
930
0
  else
931
0
  {
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
0
    WithCheckOption *wco;
937
938
0
    wco = makeNode(WithCheckOption);
939
0
    wco->kind = kind;
940
0
    wco->relname = pstrdup(RelationGetRelationName(rel));
941
0
    wco->polname = NULL;
942
0
    wco->qual = (Node *) makeConst(BOOLOID, -1, InvalidOid,
943
0
                     sizeof(bool), BoolGetDatum(false),
944
0
                     false, true);
945
0
    wco->cascaded = false;
946
947
0
    *withCheckOptions = lappend(*withCheckOptions, wco);
948
0
  }
949
0
}
950
951
/*
952
 * check_role_for_policy -
953
 *   determines if the policy should be applied for the current role
954
 */
955
static bool
956
check_role_for_policy(ArrayType *policy_roles, Oid user_id)
957
0
{
958
0
  int     i;
959
0
  Oid      *roles = (Oid *) ARR_DATA_PTR(policy_roles);
960
961
  /* Quick fall-thru for policies applied to all roles */
962
0
  if (roles[0] == ACL_ID_PUBLIC)
963
0
    return true;
964
965
0
  for (i = 0; i < ARR_DIMS(policy_roles)[0]; i++)
966
0
  {
967
0
    if (has_privs_of_role(user_id, roles[i]))
968
0
      return true;
969
0
  }
970
971
0
  return false;
972
0
}