LCOV - code coverage report
Current view: top level - src/backend/rewrite - rowsecurity.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 170 187 90.9 %
Date: 2017-09-29 13:40:31 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * rewrite/rowsecurity.c
       3             :  *    Routines to support policies for row level security (aka RLS).
       4             :  *
       5             :  * Policies in PostgreSQL provide a mechanism to limit what records are
       6             :  * returned to a user and what records a user is permitted to add to a table.
       7             :  *
       8             :  * Policies can be defined for specific roles, specific commands, or provided
       9             :  * by an extension.  Row security can also be enabled for a table without any
      10             :  * policies being explicitly defined, in which case a default-deny policy is
      11             :  * applied.
      12             :  *
      13             :  * Any part of the system which is returning records back to the user, or
      14             :  * which is accepting records from the user to add to a table, needs to
      15             :  * consider the policies associated with the table (if any).  For normal
      16             :  * queries, this is handled by calling get_row_security_policies() during
      17             :  * rewrite, for each RTE in the query.  This returns the expressions defined
      18             :  * by the table's policies as a list that is prepended to the securityQuals
      19             :  * list for the RTE.  For queries which modify the table, any WITH CHECK
      20             :  * clauses from the table's policies are also returned and prepended to the
      21             :  * list of WithCheckOptions for the Query to check each row that is being
      22             :  * added to the table.  Other parts of the system (eg: COPY) simply construct
      23             :  * a normal query and use that, if RLS is to be applied.
      24             :  *
      25             :  * The check to see if RLS should be enabled is provided through
      26             :  * check_enable_rls(), which returns an enum (defined in rowsecurity.h) to
      27             :  * indicate if RLS should be enabled (RLS_ENABLED), or bypassed (RLS_NONE or
      28             :  * RLS_NONE_ENV).  RLS_NONE_ENV indicates that RLS should be bypassed
      29             :  * in the current environment, but that may change if the row_security GUC or
      30             :  * the current role changes.
      31             :  *
      32             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
      33             :  * Portions Copyright (c) 1994, Regents of the University of California
      34             :  */
      35             : #include "postgres.h"
      36             : 
      37             : #include "access/heapam.h"
      38             : #include "access/htup_details.h"
      39             : #include "access/sysattr.h"
      40             : #include "catalog/pg_class.h"
      41             : #include "catalog/pg_inherits_fn.h"
      42             : #include "catalog/pg_policy.h"
      43             : #include "catalog/pg_type.h"
      44             : #include "miscadmin.h"
      45             : #include "nodes/makefuncs.h"
      46             : #include "nodes/nodeFuncs.h"
      47             : #include "nodes/pg_list.h"
      48             : #include "nodes/plannodes.h"
      49             : #include "parser/parsetree.h"
      50             : #include "rewrite/rewriteHandler.h"
      51             : #include "rewrite/rewriteManip.h"
      52             : #include "rewrite/rowsecurity.h"
      53             : #include "utils/acl.h"
      54             : #include "utils/lsyscache.h"
      55             : #include "utils/rel.h"
      56             : #include "utils/rls.h"
      57             : #include "utils/syscache.h"
      58             : #include "tcop/utility.h"
      59             : 
      60             : static void get_policies_for_relation(Relation relation,
      61             :                           CmdType cmd, Oid user_id,
      62             :                           List **permissive_policies,
      63             :                           List **restrictive_policies);
      64             : 
      65             : static List *sort_policies_by_name(List *policies);
      66             : 
      67             : static int  row_security_policy_cmp(const void *a, const void *b);
      68             : 
      69             : static void add_security_quals(int rt_index,
      70             :                    List *permissive_policies,
      71             :                    List *restrictive_policies,
      72             :                    List **securityQuals,
      73             :                    bool *hasSubLinks);
      74             : 
      75             : static void add_with_check_options(Relation rel,
      76             :                        int rt_index,
      77             :                        WCOKind kind,
      78             :                        List *permissive_policies,
      79             :                        List *restrictive_policies,
      80             :                        List **withCheckOptions,
      81             :                        bool *hasSubLinks,
      82             :                        bool force_using);
      83             : 
      84             : static bool check_role_for_policy(ArrayType *policy_roles, Oid user_id);
      85             : 
      86             : /*
      87             :  * hooks to allow extensions to add their own security policies
      88             :  *
      89             :  * row_security_policy_hook_permissive can be used to add policies which
      90             :  * are combined with the other permissive policies, using OR.
      91             :  *
      92             :  * row_security_policy_hook_restrictive can be used to add policies which
      93             :  * are enforced, regardless of other policies (they are combined using AND).
      94             :  */
      95             : row_security_policy_hook_type row_security_policy_hook_permissive = NULL;
      96             : row_security_policy_hook_type row_security_policy_hook_restrictive = NULL;
      97             : 
      98             : /*
      99             :  * Get any row security quals and WithCheckOption checks that should be
     100             :  * applied to the specified RTE.
     101             :  *
     102             :  * In addition, hasRowSecurity is set to true if row level security is enabled
     103             :  * (even if this RTE doesn't have any row security quals), and hasSubLinks is
     104             :  * set to true if any of the quals returned contain sublinks.
     105             :  */
     106             : void
     107       18491 : get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
     108             :                           List **securityQuals, List **withCheckOptions,
     109             :                           bool *hasRowSecurity, bool *hasSubLinks)
     110             : {
     111             :     Oid         user_id;
     112             :     int         rls_status;
     113             :     Relation    rel;
     114             :     CmdType     commandType;
     115             :     List       *permissive_policies;
     116             :     List       *restrictive_policies;
     117             : 
     118             :     /* Defaults for the return values */
     119       18491 :     *securityQuals = NIL;
     120       18491 :     *withCheckOptions = NIL;
     121       18491 :     *hasRowSecurity = false;
     122       18491 :     *hasSubLinks = false;
     123             : 
     124             :     /* If this is not a normal relation, just return immediately */
     125       18623 :     if (rte->relkind != RELKIND_RELATION &&
     126         132 :         rte->relkind != RELKIND_PARTITIONED_TABLE)
     127           0 :         return;
     128             : 
     129             :     /* Switch to checkAsUser if it's set */
     130       18491 :     user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
     131             : 
     132             :     /* Determine the state of RLS for this, pass checkAsUser explicitly */
     133       18491 :     rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false);
     134             : 
     135             :     /* If there is no RLS on this table at all, nothing to do */
     136       18483 :     if (rls_status == RLS_NONE)
     137       18130 :         return;
     138             : 
     139             :     /*
     140             :      * RLS_NONE_ENV means we are not doing any RLS now, but that may change
     141             :      * with changes to the environment, so we mark it as hasRowSecurity to
     142             :      * force a re-plan when the environment changes.
     143             :      */
     144         353 :     if (rls_status == RLS_NONE_ENV)
     145             :     {
     146             :         /*
     147             :          * Indicate that this query may involve RLS and must therefore be
     148             :          * replanned if the environment changes (GUCs, role), but we are not
     149             :          * adding anything here.
     150             :          */
     151          58 :         *hasRowSecurity = true;
     152             : 
     153          58 :         return;
     154             :     }
     155             : 
     156             :     /*
     157             :      * RLS is enabled for this relation.
     158             :      *
     159             :      * Get the security policies that should be applied, based on the command
     160             :      * type.  Note that if this isn't the target relation, we actually want
     161             :      * the relation's SELECT policies, regardless of the query command type,
     162             :      * for example in UPDATE t1 ... FROM t2 we need to apply t1's UPDATE
     163             :      * policies and t2's SELECT policies.
     164             :      */
     165         295 :     rel = heap_open(rte->relid, NoLock);
     166             : 
     167         295 :     commandType = rt_index == root->resultRelation ?
     168             :         root->commandType : CMD_SELECT;
     169             : 
     170             :     /*
     171             :      * In some cases, we need to apply USING policies (which control the
     172             :      * visibility of records) associated with multiple command types (see
     173             :      * specific cases below).
     174             :      *
     175             :      * When considering the order in which to apply these USING policies, we
     176             :      * prefer to apply higher privileged policies, those which allow the user
     177             :      * to lock records (UPDATE and DELETE), first, followed by policies which
     178             :      * don't (SELECT).
     179             :      *
     180             :      * Note that the optimizer is free to push down and reorder quals which
     181             :      * use leakproof functions.
     182             :      *
     183             :      * In all cases, if there are no policy clauses allowing access to rows in
     184             :      * the table for the specific type of operation, then a single
     185             :      * always-false clause (a default-deny policy) will be added (see
     186             :      * add_security_quals).
     187             :      */
     188             : 
     189             :     /*
     190             :      * For a SELECT, if UPDATE privileges are required (eg: the user has
     191             :      * specified FOR [KEY] UPDATE/SHARE), then add the UPDATE USING quals
     192             :      * first.
     193             :      *
     194             :      * This way, we filter out any records from the SELECT FOR SHARE/UPDATE
     195             :      * which the user does not have access to via the UPDATE USING policies,
     196             :      * similar to how we require normal UPDATE rights for these queries.
     197             :      */
     198         295 :     if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE)
     199             :     {
     200             :         List       *update_permissive_policies;
     201             :         List       *update_restrictive_policies;
     202             : 
     203           4 :         get_policies_for_relation(rel, CMD_UPDATE, user_id,
     204             :                                   &update_permissive_policies,
     205             :                                   &update_restrictive_policies);
     206             : 
     207           4 :         add_security_quals(rt_index,
     208             :                            update_permissive_policies,
     209             :                            update_restrictive_policies,
     210             :                            securityQuals,
     211             :                            hasSubLinks);
     212             :     }
     213             : 
     214             :     /*
     215             :      * For SELECT, UPDATE and DELETE, add security quals to enforce the USING
     216             :      * policies.  These security quals control access to existing table rows.
     217             :      * Restrictive policies are combined together using AND, and permissive
     218             :      * policies are combined together using OR.
     219             :      */
     220             : 
     221         295 :     get_policies_for_relation(rel, commandType, user_id, &permissive_policies,
     222             :                               &restrictive_policies);
     223             : 
     224         295 :     if (commandType == CMD_SELECT ||
     225          48 :         commandType == CMD_UPDATE ||
     226             :         commandType == CMD_DELETE)
     227         260 :         add_security_quals(rt_index,
     228             :                            permissive_policies,
     229             :                            restrictive_policies,
     230             :                            securityQuals,
     231             :                            hasSubLinks);
     232             : 
     233             :     /*
     234             :      * Similar to above, during an UPDATE or DELETE, if SELECT rights are also
     235             :      * required (eg: when a RETURNING clause exists, or the user has provided
     236             :      * a WHERE clause which involves columns from the relation), we collect up
     237             :      * CMD_SELECT policies and add them via add_security_quals first.
     238             :      *
     239             :      * This way, we filter out any records which are not visible through an
     240             :      * ALL or SELECT USING policy.
     241             :      */
     242         341 :     if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) &&
     243          46 :         rte->requiredPerms & ACL_SELECT)
     244             :     {
     245             :         List       *select_permissive_policies;
     246             :         List       *select_restrictive_policies;
     247             : 
     248          43 :         get_policies_for_relation(rel, CMD_SELECT, user_id,
     249             :                                   &select_permissive_policies,
     250             :                                   &select_restrictive_policies);
     251             : 
     252          43 :         add_security_quals(rt_index,
     253             :                            select_permissive_policies,
     254             :                            select_restrictive_policies,
     255             :                            securityQuals,
     256             :                            hasSubLinks);
     257             :     }
     258             : 
     259             :     /*
     260             :      * For INSERT and UPDATE, add withCheckOptions to verify that any new
     261             :      * records added are consistent with the security policies.  This will use
     262             :      * each policy's WITH CHECK clause, or its USING clause if no explicit
     263             :      * WITH CHECK clause is defined.
     264             :      */
     265         295 :     if (commandType == CMD_INSERT || commandType == CMD_UPDATE)
     266             :     {
     267             :         /* This should be the target relation */
     268          68 :         Assert(rt_index == root->resultRelation);
     269             : 
     270          68 :         add_with_check_options(rel, rt_index,
     271             :                                commandType == CMD_INSERT ?
     272             :                                WCO_RLS_INSERT_CHECK : WCO_RLS_UPDATE_CHECK,
     273             :                                permissive_policies,
     274             :                                restrictive_policies,
     275             :                                withCheckOptions,
     276             :                                hasSubLinks,
     277             :                                false);
     278             : 
     279             :         /*
     280             :          * Get and add ALL/SELECT policies, if SELECT rights are required for
     281             :          * this relation (eg: when RETURNING is used).  These are added as WCO
     282             :          * policies rather than security quals to ensure that an error is
     283             :          * raised if a policy is violated; otherwise, we might end up silently
     284             :          * dropping rows to be added.
     285             :          */
     286          68 :         if (rte->requiredPerms & ACL_SELECT)
     287             :         {
     288          48 :             List       *select_permissive_policies = NIL;
     289          48 :             List       *select_restrictive_policies = NIL;
     290             : 
     291          48 :             get_policies_for_relation(rel, CMD_SELECT, user_id,
     292             :                                       &select_permissive_policies,
     293             :                                       &select_restrictive_policies);
     294          48 :             add_with_check_options(rel, rt_index,
     295             :                                    commandType == CMD_INSERT ?
     296             :                                    WCO_RLS_INSERT_CHECK : WCO_RLS_UPDATE_CHECK,
     297             :                                    select_permissive_policies,
     298             :                                    select_restrictive_policies,
     299             :                                    withCheckOptions,
     300             :                                    hasSubLinks,
     301             :                                    true);
     302             :         }
     303             : 
     304             :         /*
     305             :          * For INSERT ... ON CONFLICT DO UPDATE we need additional policy
     306             :          * checks for the UPDATE which may be applied to the same RTE.
     307             :          */
     308         103 :         if (commandType == CMD_INSERT &&
     309          48 :             root->onConflict && root->onConflict->action == ONCONFLICT_UPDATE)
     310             :         {
     311             :             List       *conflict_permissive_policies;
     312             :             List       *conflict_restrictive_policies;
     313             : 
     314             :             /* Get the policies that apply to the auxiliary UPDATE */
     315          13 :             get_policies_for_relation(rel, CMD_UPDATE, user_id,
     316             :                                       &conflict_permissive_policies,
     317             :                                       &conflict_restrictive_policies);
     318             : 
     319             :             /*
     320             :              * Enforce the USING clauses of the UPDATE policies using WCOs
     321             :              * rather than security quals.  This ensures that an error is
     322             :              * raised if the conflicting row cannot be updated due to RLS,
     323             :              * rather than the change being silently dropped.
     324             :              */
     325          13 :             add_with_check_options(rel, rt_index,
     326             :                                    WCO_RLS_CONFLICT_CHECK,
     327             :                                    conflict_permissive_policies,
     328             :                                    conflict_restrictive_policies,
     329             :                                    withCheckOptions,
     330             :                                    hasSubLinks,
     331             :                                    true);
     332             : 
     333             :             /*
     334             :              * Get and add ALL/SELECT policies, as WCO_RLS_CONFLICT_CHECK WCOs
     335             :              * to ensure they are considered when taking the UPDATE path of an
     336             :              * INSERT .. ON CONFLICT DO UPDATE, if SELECT rights are required
     337             :              * for this relation, also as WCO policies, again, to avoid
     338             :              * silently dropping data.  See above.
     339             :              */
     340          13 :             if (rte->requiredPerms & ACL_SELECT)
     341             :             {
     342          13 :                 List       *conflict_select_permissive_policies = NIL;
     343          13 :                 List       *conflict_select_restrictive_policies = NIL;
     344             : 
     345          13 :                 get_policies_for_relation(rel, CMD_SELECT, user_id,
     346             :                                           &conflict_select_permissive_policies,
     347             :                                           &conflict_select_restrictive_policies);
     348          13 :                 add_with_check_options(rel, rt_index,
     349             :                                        WCO_RLS_CONFLICT_CHECK,
     350             :                                        conflict_select_permissive_policies,
     351             :                                        conflict_select_restrictive_policies,
     352             :                                        withCheckOptions,
     353             :                                        hasSubLinks,
     354             :                                        true);
     355             :             }
     356             : 
     357             :             /* Enforce the WITH CHECK clauses of the UPDATE policies */
     358          13 :             add_with_check_options(rel, rt_index,
     359             :                                    WCO_RLS_UPDATE_CHECK,
     360             :                                    conflict_permissive_policies,
     361             :                                    conflict_restrictive_policies,
     362             :                                    withCheckOptions,
     363             :                                    hasSubLinks,
     364             :                                    false);
     365             :         }
     366             :     }
     367             : 
     368         295 :     heap_close(rel, NoLock);
     369             : 
     370             :     /*
     371             :      * Mark this query as having row security, so plancache can invalidate it
     372             :      * when necessary (eg: role changes)
     373             :      */
     374         295 :     *hasRowSecurity = true;
     375             : 
     376         295 :     return;
     377             : }
     378             : 
     379             : /*
     380             :  * get_policies_for_relation
     381             :  *
     382             :  * Returns lists of permissive and restrictive policies to be applied to the
     383             :  * specified relation, based on the command type and role.
     384             :  *
     385             :  * This includes any policies added by extensions.
     386             :  */
     387             : static void
     388         416 : get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
     389             :                           List **permissive_policies,
     390             :                           List **restrictive_policies)
     391             : {
     392             :     ListCell   *item;
     393             : 
     394         416 :     *permissive_policies = NIL;
     395         416 :     *restrictive_policies = NIL;
     396             : 
     397             :     /*
     398             :      * First find all internal policies for the relation.  CREATE POLICY does
     399             :      * not currently support defining restrictive policies, so for now all
     400             :      * internal policies are permissive.
     401             :      */
     402        1200 :     foreach(item, relation->rd_rsdesc->policies)
     403             :     {
     404         784 :         bool        cmd_matches = false;
     405         784 :         RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     406             : 
     407             :         /* Always add ALL policies, if they exist. */
     408         784 :         if (policy->polcmd == '*')
     409         502 :             cmd_matches = true;
     410             :         else
     411             :         {
     412             :             /* Check whether the policy applies to the specified command type */
     413         282 :             switch (cmd)
     414             :             {
     415             :                 case CMD_SELECT:
     416         176 :                     if (policy->polcmd == ACL_SELECT_CHR)
     417          58 :                         cmd_matches = true;
     418         176 :                     break;
     419             :                 case CMD_INSERT:
     420          38 :                     if (policy->polcmd == ACL_INSERT_CHR)
     421          13 :                         cmd_matches = true;
     422          38 :                     break;
     423             :                 case CMD_UPDATE:
     424          54 :                     if (policy->polcmd == ACL_UPDATE_CHR)
     425          19 :                         cmd_matches = true;
     426          54 :                     break;
     427             :                 case CMD_DELETE:
     428          14 :                     if (policy->polcmd == ACL_DELETE_CHR)
     429           4 :                         cmd_matches = true;
     430          14 :                     break;
     431             :                 default:
     432           0 :                     elog(ERROR, "unrecognized policy command type %d",
     433             :                          (int) cmd);
     434             :                     break;
     435             :             }
     436             :         }
     437             : 
     438             :         /*
     439             :          * Add this policy to the list of permissive policies if it applies to
     440             :          * the specified role.
     441             :          */
     442         784 :         if (cmd_matches && check_role_for_policy(policy->roles, user_id))
     443             :         {
     444         455 :             if (policy->permissive)
     445         432 :                 *permissive_policies = lappend(*permissive_policies, policy);
     446             :             else
     447          23 :                 *restrictive_policies = lappend(*restrictive_policies, policy);
     448             :         }
     449             :     }
     450             : 
     451             :     /*
     452             :      * We sort restrictive policies by name so that any WCOs they generate are
     453             :      * checked in a well-defined order.
     454             :      */
     455         416 :     *restrictive_policies = sort_policies_by_name(*restrictive_policies);
     456             : 
     457             :     /*
     458             :      * Then add any permissive or restrictive policies defined by extensions.
     459             :      * These are simply appended to the lists of internal policies, if they
     460             :      * apply to the specified role.
     461             :      */
     462         416 :     if (row_security_policy_hook_restrictive)
     463             :     {
     464           0 :         List       *hook_policies =
     465           0 :         (*row_security_policy_hook_restrictive) (cmd, relation);
     466             : 
     467             :         /*
     468             :          * As with built-in restrictive policies, we sort any hook-provided
     469             :          * restrictive policies by name also.  Note that we also intentionally
     470             :          * always check all built-in restrictive policies, in name order,
     471             :          * before checking restrictive policies added by hooks, in name order.
     472             :          */
     473           0 :         hook_policies = sort_policies_by_name(hook_policies);
     474             : 
     475           0 :         foreach(item, hook_policies)
     476             :         {
     477           0 :             RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     478             : 
     479           0 :             if (check_role_for_policy(policy->roles, user_id))
     480           0 :                 *restrictive_policies = lappend(*restrictive_policies, policy);
     481             :         }
     482             :     }
     483             : 
     484         416 :     if (row_security_policy_hook_permissive)
     485             :     {
     486           0 :         List       *hook_policies =
     487           0 :         (*row_security_policy_hook_permissive) (cmd, relation);
     488             : 
     489           0 :         foreach(item, hook_policies)
     490             :         {
     491           0 :             RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     492             : 
     493           0 :             if (check_role_for_policy(policy->roles, user_id))
     494           0 :                 *permissive_policies = lappend(*permissive_policies, policy);
     495             :         }
     496             :     }
     497         416 : }
     498             : 
     499             : /*
     500             :  * sort_policies_by_name
     501             :  *
     502             :  * This is only used for restrictive policies, ensuring that any
     503             :  * WithCheckOptions they generate are applied in a well-defined order.
     504             :  * This is not necessary for permissive policies, since they are all combined
     505             :  * together using OR into a single WithCheckOption check.
     506             :  */
     507             : static List *
     508         416 : sort_policies_by_name(List *policies)
     509             : {
     510         416 :     int         npol = list_length(policies);
     511             :     RowSecurityPolicy *pols;
     512             :     ListCell   *item;
     513         416 :     int         ii = 0;
     514             : 
     515         416 :     if (npol <= 1)
     516         410 :         return policies;
     517             : 
     518           6 :     pols = (RowSecurityPolicy *) palloc(sizeof(RowSecurityPolicy) * npol);
     519             : 
     520          18 :     foreach(item, policies)
     521             :     {
     522          12 :         RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     523             : 
     524          12 :         pols[ii++] = *policy;
     525             :     }
     526             : 
     527           6 :     qsort(pols, npol, sizeof(RowSecurityPolicy), row_security_policy_cmp);
     528             : 
     529           6 :     policies = NIL;
     530          18 :     for (ii = 0; ii < npol; ii++)
     531          12 :         policies = lappend(policies, &pols[ii]);
     532             : 
     533           6 :     return policies;
     534             : }
     535             : 
     536             : /*
     537             :  * qsort comparator to sort RowSecurityPolicy entries by name
     538             :  */
     539             : static int
     540           6 : row_security_policy_cmp(const void *a, const void *b)
     541             : {
     542           6 :     const RowSecurityPolicy *pa = (const RowSecurityPolicy *) a;
     543           6 :     const RowSecurityPolicy *pb = (const RowSecurityPolicy *) b;
     544             : 
     545             :     /* Guard against NULL policy names from extensions */
     546           6 :     if (pa->policy_name == NULL)
     547           0 :         return pb->policy_name == NULL ? 0 : 1;
     548           6 :     if (pb->policy_name == NULL)
     549           0 :         return -1;
     550             : 
     551           6 :     return strcmp(pa->policy_name, pb->policy_name);
     552             : }
     553             : 
     554             : /*
     555             :  * add_security_quals
     556             :  *
     557             :  * Add security quals to enforce the specified RLS policies, restricting
     558             :  * access to existing data in a table.  If there are no policies controlling
     559             :  * access to the table, then all access is prohibited --- i.e., an implicit
     560             :  * default-deny policy is used.
     561             :  *
     562             :  * New security quals are added to securityQuals, and hasSubLinks is set to
     563             :  * true if any of the quals added contain sublink subqueries.
     564             :  */
     565             : static void
     566         307 : add_security_quals(int rt_index,
     567             :                    List *permissive_policies,
     568             :                    List *restrictive_policies,
     569             :                    List **securityQuals,
     570             :                    bool *hasSubLinks)
     571             : {
     572             :     ListCell   *item;
     573         307 :     List       *permissive_quals = NIL;
     574             :     Expr       *rowsec_expr;
     575             : 
     576             :     /*
     577             :      * First collect up the permissive quals.  If we do not find any
     578             :      * permissive policies then no rows are visible (this is handled below).
     579             :      */
     580         634 :     foreach(item, permissive_policies)
     581             :     {
     582         327 :         RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     583             : 
     584         327 :         if (policy->qual != NULL)
     585             :         {
     586         327 :             permissive_quals = lappend(permissive_quals,
     587         327 :                                        copyObject(policy->qual));
     588         327 :             *hasSubLinks |= policy->hassublinks;
     589             :         }
     590             :     }
     591             : 
     592             :     /*
     593             :      * We must have permissive quals, always, or no rows are visible.
     594             :      *
     595             :      * If we do not, then we simply return a single 'false' qual which results
     596             :      * in no rows being visible.
     597             :      */
     598         307 :     if (permissive_quals != NIL)
     599             :     {
     600             :         /*
     601             :          * We now know that permissive policies exist, so we can now add
     602             :          * security quals based on the USING clauses from the restrictive
     603             :          * policies.  Since these need to be combined together using AND, we
     604             :          * can just add them one at a time.
     605             :          */
     606         315 :         foreach(item, restrictive_policies)
     607             :         {
     608          13 :             RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     609             :             Expr       *qual;
     610             : 
     611          13 :             if (policy->qual != NULL)
     612             :             {
     613          13 :                 qual = copyObject(policy->qual);
     614          13 :                 ChangeVarNodes((Node *) qual, 1, rt_index, 0);
     615             : 
     616          13 :                 *securityQuals = list_append_unique(*securityQuals, qual);
     617          13 :                 *hasSubLinks |= policy->hassublinks;
     618             :             }
     619             :         }
     620             : 
     621             :         /*
     622             :          * Then add a single security qual combining together the USING
     623             :          * clauses from all the permissive policies using OR.
     624             :          */
     625         302 :         if (list_length(permissive_quals) == 1)
     626         285 :             rowsec_expr = (Expr *) linitial(permissive_quals);
     627             :         else
     628          17 :             rowsec_expr = makeBoolExpr(OR_EXPR, permissive_quals, -1);
     629             : 
     630         302 :         ChangeVarNodes((Node *) rowsec_expr, 1, rt_index, 0);
     631         302 :         *securityQuals = list_append_unique(*securityQuals, rowsec_expr);
     632             :     }
     633             :     else
     634             : 
     635             :         /*
     636             :          * A permissive policy must exist for rows to be visible at all.
     637             :          * Therefore, if there were no permissive policies found, return a
     638             :          * single always-false clause.
     639             :          */
     640           5 :         *securityQuals = lappend(*securityQuals,
     641           5 :                                  makeConst(BOOLOID, -1, InvalidOid,
     642             :                                            sizeof(bool), BoolGetDatum(false),
     643             :                                            false, true));
     644         307 : }
     645             : 
     646             : /*
     647             :  * add_with_check_options
     648             :  *
     649             :  * Add WithCheckOptions of the specified kind to check that new records
     650             :  * added by an INSERT or UPDATE are consistent with the specified RLS
     651             :  * policies.  Normally new data must satisfy the WITH CHECK clauses from the
     652             :  * policies.  If a policy has no explicit WITH CHECK clause, its USING clause
     653             :  * is used instead.  In the special case of an UPDATE arising from an
     654             :  * INSERT ... ON CONFLICT DO UPDATE, existing records are first checked using
     655             :  * a WCO_RLS_CONFLICT_CHECK WithCheckOption, which always uses the USING
     656             :  * clauses from RLS policies.
     657             :  *
     658             :  * New WCOs are added to withCheckOptions, and hasSubLinks is set to true if
     659             :  * any of the check clauses added contain sublink subqueries.
     660             :  */
     661             : static void
     662         155 : add_with_check_options(Relation rel,
     663             :                        int rt_index,
     664             :                        WCOKind kind,
     665             :                        List *permissive_policies,
     666             :                        List *restrictive_policies,
     667             :                        List **withCheckOptions,
     668             :                        bool *hasSubLinks,
     669             :                        bool force_using)
     670             : {
     671             :     ListCell   *item;
     672         155 :     List       *permissive_quals = NIL;
     673             : 
     674             : #define QUAL_FOR_WCO(policy) \
     675             :     ( !force_using && \
     676             :       (policy)->with_check_qual != NULL ? \
     677             :       (policy)->with_check_qual : (policy)->qual )
     678             : 
     679             :     /*
     680             :      * First collect up the permissive policy clauses, similar to
     681             :      * add_security_quals.
     682             :      */
     683         308 :     foreach(item, permissive_policies)
     684             :     {
     685         153 :         RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     686         153 :         Expr       *qual = QUAL_FOR_WCO(policy);
     687             : 
     688         153 :         if (qual != NULL)
     689             :         {
     690         153 :             permissive_quals = lappend(permissive_quals, copyObject(qual));
     691         153 :             *hasSubLinks |= policy->hassublinks;
     692             :         }
     693             :     }
     694             : 
     695             :     /*
     696             :      * There must be at least one permissive qual found or no rows are allowed
     697             :      * to be added.  This is the same as in add_security_quals.
     698             :      *
     699             :      * If there are no permissive_quals then we fall through and return a
     700             :      * single 'false' WCO, preventing all new rows.
     701             :      */
     702         155 :     if (permissive_quals != NIL)
     703             :     {
     704             :         /*
     705             :          * Add a single WithCheckOption for all the permissive policy clauses,
     706             :          * combining them together using OR.  This check has no policy name,
     707             :          * since if the check fails it means that no policy granted permission
     708             :          * to perform the update, rather than any particular policy being
     709             :          * violated.
     710             :          */
     711             :         WithCheckOption *wco;
     712             : 
     713         148 :         wco = makeNode(WithCheckOption);
     714         148 :         wco->kind = kind;
     715         148 :         wco->relname = pstrdup(RelationGetRelationName(rel));
     716         148 :         wco->polname = NULL;
     717         148 :         wco->cascaded = false;
     718             : 
     719         148 :         if (list_length(permissive_quals) == 1)
     720         143 :             wco->qual = (Node *) linitial(permissive_quals);
     721             :         else
     722           5 :             wco->qual = (Node *) makeBoolExpr(OR_EXPR, permissive_quals, -1);
     723             : 
     724         148 :         ChangeVarNodes(wco->qual, 1, rt_index, 0);
     725             : 
     726         148 :         *withCheckOptions = list_append_unique(*withCheckOptions, wco);
     727             : 
     728             :         /*
     729             :          * Now add WithCheckOptions for each of the restrictive policy clauses
     730             :          * (which will be combined together using AND).  We use a separate
     731             :          * WithCheckOption for each restrictive policy to allow the policy
     732             :          * name to be included in error reports if the policy is violated.
     733             :          */
     734         156 :         foreach(item, restrictive_policies)
     735             :         {
     736           8 :             RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     737           8 :             Expr       *qual = QUAL_FOR_WCO(policy);
     738             :             WithCheckOption *wco;
     739             : 
     740           8 :             if (qual != NULL)
     741             :             {
     742           8 :                 qual = copyObject(qual);
     743           8 :                 ChangeVarNodes((Node *) qual, 1, rt_index, 0);
     744             : 
     745           8 :                 wco = makeNode(WithCheckOption);
     746           8 :                 wco->kind = kind;
     747           8 :                 wco->relname = pstrdup(RelationGetRelationName(rel));
     748           8 :                 wco->polname = pstrdup(policy->policy_name);
     749           8 :                 wco->qual = (Node *) qual;
     750           8 :                 wco->cascaded = false;
     751             : 
     752           8 :                 *withCheckOptions = list_append_unique(*withCheckOptions, wco);
     753           8 :                 *hasSubLinks |= policy->hassublinks;
     754             :             }
     755             :         }
     756             :     }
     757             :     else
     758             :     {
     759             :         /*
     760             :          * If there were no policy clauses to check new data, add a single
     761             :          * always-false WCO (a default-deny policy).
     762             :          */
     763             :         WithCheckOption *wco;
     764             : 
     765           7 :         wco = makeNode(WithCheckOption);
     766           7 :         wco->kind = kind;
     767           7 :         wco->relname = pstrdup(RelationGetRelationName(rel));
     768           7 :         wco->polname = NULL;
     769           7 :         wco->qual = (Node *) makeConst(BOOLOID, -1, InvalidOid,
     770             :                                        sizeof(bool), BoolGetDatum(false),
     771             :                                        false, true);
     772           7 :         wco->cascaded = false;
     773             : 
     774           7 :         *withCheckOptions = lappend(*withCheckOptions, wco);
     775             :     }
     776         155 : }
     777             : 
     778             : /*
     779             :  * check_role_for_policy -
     780             :  *   determines if the policy should be applied for the current role
     781             :  */
     782             : static bool
     783         596 : check_role_for_policy(ArrayType *policy_roles, Oid user_id)
     784             : {
     785             :     int         i;
     786         596 :     Oid        *roles = (Oid *) ARR_DATA_PTR(policy_roles);
     787             : 
     788             :     /* Quick fall-thru for policies applied to all roles */
     789         596 :     if (roles[0] == ACL_ID_PUBLIC)
     790         404 :         return true;
     791             : 
     792         333 :     for (i = 0; i < ARR_DIMS(policy_roles)[0]; i++)
     793             :     {
     794         192 :         if (has_privs_of_role(user_id, roles[i]))
     795          51 :             return true;
     796             :     }
     797             : 
     798         141 :     return false;
     799             : }

Generated by: LCOV version 1.11