Coverage Report

Created: 2025-06-13 06:06

/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
}