LCOV - code coverage report
Current view: top level - src/backend/rewrite - rowsecurity.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12devel Lines: 185 189 97.9 %
Date: 2018-09-20 15:21:09 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-2018, 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.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      237702 : 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      237702 :     *securityQuals = NIL;
     120      237702 :     *withCheckOptions = NIL;
     121      237702 :     *hasRowSecurity = false;
     122      237702 :     *hasSubLinks = false;
     123             : 
     124             :     /* If this is not a normal relation, just return immediately */
     125      241588 :     if (rte->relkind != RELKIND_RELATION &&
     126        3886 :         rte->relkind != RELKIND_PARTITIONED_TABLE)
     127           0 :         return;
     128             : 
     129             :     /* Switch to checkAsUser if it's set */
     130      237702 :     user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
     131             : 
     132             :     /* Determine the state of RLS for this, pass checkAsUser explicitly */
     133      237702 :     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      237670 :     if (rls_status == RLS_NONE)
     137      236072 :         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        1598 :     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         306 :         *hasRowSecurity = true;
     152             : 
     153         306 :         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        1292 :     rel = heap_open(rte->relid, NoLock);
     166             : 
     167        2584 :     commandType = rt_index == root->resultRelation ?
     168        1292 :         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        1292 :     if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE)
     199             :     {
     200             :         List       *update_permissive_policies;
     201             :         List       *update_restrictive_policies;
     202             : 
     203          16 :         get_policies_for_relation(rel, CMD_UPDATE, user_id,
     204             :                                   &update_permissive_policies,
     205             :                                   &update_restrictive_policies);
     206             : 
     207          16 :         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        1292 :     get_policies_for_relation(rel, commandType, user_id, &permissive_policies,
     222             :                               &restrictive_policies);
     223             : 
     224        1292 :     if (commandType == CMD_SELECT ||
     225         240 :         commandType == CMD_UPDATE ||
     226             :         commandType == CMD_DELETE)
     227        1104 :         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        1504 :     if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) &&
     243         212 :         rte->requiredPerms & ACL_SELECT)
     244             :     {
     245             :         List       *select_permissive_policies;
     246             :         List       *select_restrictive_policies;
     247             : 
     248         200 :         get_policies_for_relation(rel, CMD_SELECT, user_id,
     249             :                                   &select_permissive_policies,
     250             :                                   &select_restrictive_policies);
     251             : 
     252         200 :         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        1292 :     if (commandType == CMD_INSERT || commandType == CMD_UPDATE)
     266             :     {
     267             :         /* This should be the target relation */
     268             :         Assert(rt_index == root->resultRelation);
     269             : 
     270         348 :         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         348 :         if (rte->requiredPerms & ACL_SELECT)
     287             :         {
     288         232 :             List       *select_permissive_policies = NIL;
     289         232 :             List       *select_restrictive_policies = NIL;
     290             : 
     291         232 :             get_policies_for_relation(rel, CMD_SELECT, user_id,
     292             :                                       &select_permissive_policies,
     293             :                                       &select_restrictive_policies);
     294         232 :             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         536 :         if (commandType == CMD_INSERT &&
     309         252 :             root->onConflict && root->onConflict->action == ONCONFLICT_UPDATE)
     310             :         {
     311             :             List       *conflict_permissive_policies;
     312             :             List       *conflict_restrictive_policies;
     313          64 :             List       *conflict_select_permissive_policies = NIL;
     314          64 :             List       *conflict_select_restrictive_policies = NIL;
     315             : 
     316             :             /* Get the policies that apply to the auxiliary UPDATE */
     317          64 :             get_policies_for_relation(rel, CMD_UPDATE, user_id,
     318             :                                       &conflict_permissive_policies,
     319             :                                       &conflict_restrictive_policies);
     320             : 
     321             :             /*
     322             :              * Enforce the USING clauses of the UPDATE policies using WCOs
     323             :              * rather than security quals.  This ensures that an error is
     324             :              * raised if the conflicting row cannot be updated due to RLS,
     325             :              * rather than the change being silently dropped.
     326             :              */
     327          64 :             add_with_check_options(rel, rt_index,
     328             :                                    WCO_RLS_CONFLICT_CHECK,
     329             :                                    conflict_permissive_policies,
     330             :                                    conflict_restrictive_policies,
     331             :                                    withCheckOptions,
     332             :                                    hasSubLinks,
     333             :                                    true);
     334             : 
     335             :             /*
     336             :              * Get and add ALL/SELECT policies, as WCO_RLS_CONFLICT_CHECK WCOs
     337             :              * to ensure they are considered when taking the UPDATE path of an
     338             :              * INSERT .. ON CONFLICT DO UPDATE, if SELECT rights are required
     339             :              * for this relation, also as WCO policies, again, to avoid
     340             :              * silently dropping data.  See above.
     341             :              */
     342          64 :             if (rte->requiredPerms & ACL_SELECT)
     343             :             {
     344          64 :                 get_policies_for_relation(rel, CMD_SELECT, user_id,
     345             :                                           &conflict_select_permissive_policies,
     346             :                                           &conflict_select_restrictive_policies);
     347          64 :                 add_with_check_options(rel, rt_index,
     348             :                                        WCO_RLS_CONFLICT_CHECK,
     349             :                                        conflict_select_permissive_policies,
     350             :                                        conflict_select_restrictive_policies,
     351             :                                        withCheckOptions,
     352             :                                        hasSubLinks,
     353             :                                        true);
     354             :             }
     355             : 
     356             :             /* Enforce the WITH CHECK clauses of the UPDATE policies */
     357          64 :             add_with_check_options(rel, rt_index,
     358             :                                    WCO_RLS_UPDATE_CHECK,
     359             :                                    conflict_permissive_policies,
     360             :                                    conflict_restrictive_policies,
     361             :                                    withCheckOptions,
     362             :                                    hasSubLinks,
     363             :                                    false);
     364             : 
     365             :             /*
     366             :              * Add ALL/SELECT policies as WCO_RLS_UPDATE_CHECK WCOs, to ensure
     367             :              * that the final updated row is visible when taking the UPDATE
     368             :              * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights
     369             :              * are required for this relation.
     370             :              */
     371          64 :             if (rte->requiredPerms & ACL_SELECT)
     372          64 :                 add_with_check_options(rel, rt_index,
     373             :                                        WCO_RLS_UPDATE_CHECK,
     374             :                                        conflict_select_permissive_policies,
     375             :                                        conflict_select_restrictive_policies,
     376             :                                        withCheckOptions,
     377             :                                        hasSubLinks,
     378             :                                        true);
     379             :         }
     380             :     }
     381             : 
     382        1292 :     heap_close(rel, NoLock);
     383             : 
     384             :     /*
     385             :      * Mark this query as having row security, so plancache can invalidate it
     386             :      * when necessary (eg: role changes)
     387             :      */
     388        1292 :     *hasRowSecurity = true;
     389             : 
     390        1292 :     return;
     391             : }
     392             : 
     393             : /*
     394             :  * get_policies_for_relation
     395             :  *
     396             :  * Returns lists of permissive and restrictive policies to be applied to the
     397             :  * specified relation, based on the command type and role.
     398             :  *
     399             :  * This includes any policies added by extensions.
     400             :  */
     401             : static void
     402        1868 : get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
     403             :                           List **permissive_policies,
     404             :                           List **restrictive_policies)
     405             : {
     406             :     ListCell   *item;
     407             : 
     408        1868 :     *permissive_policies = NIL;
     409        1868 :     *restrictive_policies = NIL;
     410             : 
     411             :     /* First find all internal policies for the relation. */
     412        5458 :     foreach(item, relation->rd_rsdesc->policies)
     413             :     {
     414        3590 :         bool        cmd_matches = false;
     415        3590 :         RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     416             : 
     417             :         /* Always add ALL policies, if they exist. */
     418        3590 :         if (policy->polcmd == '*')
     419        2062 :             cmd_matches = true;
     420             :         else
     421             :         {
     422             :             /* Check whether the policy applies to the specified command type */
     423        1528 :             switch (cmd)
     424             :             {
     425             :                 case CMD_SELECT:
     426         944 :                     if (policy->polcmd == ACL_SELECT_CHR)
     427         312 :                         cmd_matches = true;
     428         944 :                     break;
     429             :                 case CMD_INSERT:
     430         188 :                     if (policy->polcmd == ACL_INSERT_CHR)
     431          64 :                         cmd_matches = true;
     432         188 :                     break;
     433             :                 case CMD_UPDATE:
     434         340 :                     if (policy->polcmd == ACL_UPDATE_CHR)
     435         140 :                         cmd_matches = true;
     436         340 :                     break;
     437             :                 case CMD_DELETE:
     438          56 :                     if (policy->polcmd == ACL_DELETE_CHR)
     439          16 :                         cmd_matches = true;
     440          56 :                     break;
     441             :                 default:
     442           0 :                     elog(ERROR, "unrecognized policy command type %d",
     443             :                          (int) cmd);
     444             :                     break;
     445             :             }
     446             :         }
     447             : 
     448             :         /*
     449             :          * Add this policy to the relevant list of policies if it applies to
     450             :          * the specified role.
     451             :          */
     452        3590 :         if (cmd_matches && check_role_for_policy(policy->roles, user_id))
     453             :         {
     454        2030 :             if (policy->permissive)
     455        1914 :                 *permissive_policies = lappend(*permissive_policies, policy);
     456             :             else
     457         116 :                 *restrictive_policies = lappend(*restrictive_policies, policy);
     458             :         }
     459             :     }
     460             : 
     461             :     /*
     462             :      * We sort restrictive policies by name so that any WCOs they generate are
     463             :      * checked in a well-defined order.
     464             :      */
     465        1868 :     *restrictive_policies = sort_policies_by_name(*restrictive_policies);
     466             : 
     467             :     /*
     468             :      * Then add any permissive or restrictive policies defined by extensions.
     469             :      * These are simply appended to the lists of internal policies, if they
     470             :      * apply to the specified role.
     471             :      */
     472        1868 :     if (row_security_policy_hook_restrictive)
     473             :     {
     474          60 :         List       *hook_policies =
     475          60 :         (*row_security_policy_hook_restrictive) (cmd, relation);
     476             : 
     477             :         /*
     478             :          * As with built-in restrictive policies, we sort any hook-provided
     479             :          * restrictive policies by name also.  Note that we also intentionally
     480             :          * always check all built-in restrictive policies, in name order,
     481             :          * before checking restrictive policies added by hooks, in name order.
     482             :          */
     483          60 :         hook_policies = sort_policies_by_name(hook_policies);
     484             : 
     485         102 :         foreach(item, hook_policies)
     486             :         {
     487          42 :             RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     488             : 
     489          42 :             if (check_role_for_policy(policy->roles, user_id))
     490          42 :                 *restrictive_policies = lappend(*restrictive_policies, policy);
     491             :         }
     492             :     }
     493             : 
     494        1868 :     if (row_security_policy_hook_permissive)
     495             :     {
     496          60 :         List       *hook_policies =
     497          60 :         (*row_security_policy_hook_permissive) (cmd, relation);
     498             : 
     499         100 :         foreach(item, hook_policies)
     500             :         {
     501          40 :             RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     502             : 
     503          40 :             if (check_role_for_policy(policy->roles, user_id))
     504          40 :                 *permissive_policies = lappend(*permissive_policies, policy);
     505             :         }
     506             :     }
     507        1868 : }
     508             : 
     509             : /*
     510             :  * sort_policies_by_name
     511             :  *
     512             :  * This is only used for restrictive policies, ensuring that any
     513             :  * WithCheckOptions they generate are applied in a well-defined order.
     514             :  * This is not necessary for permissive policies, since they are all combined
     515             :  * together using OR into a single WithCheckOption check.
     516             :  */
     517             : static List *
     518        1928 : sort_policies_by_name(List *policies)
     519             : {
     520        1928 :     int         npol = list_length(policies);
     521             :     RowSecurityPolicy *pols;
     522             :     ListCell   *item;
     523        1928 :     int         ii = 0;
     524             : 
     525        1928 :     if (npol <= 1)
     526        1896 :         return policies;
     527             : 
     528          32 :     pols = (RowSecurityPolicy *) palloc(sizeof(RowSecurityPolicy) * npol);
     529             : 
     530          96 :     foreach(item, policies)
     531             :     {
     532          64 :         RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     533             : 
     534          64 :         pols[ii++] = *policy;
     535             :     }
     536             : 
     537          32 :     qsort(pols, npol, sizeof(RowSecurityPolicy), row_security_policy_cmp);
     538             : 
     539          32 :     policies = NIL;
     540          96 :     for (ii = 0; ii < npol; ii++)
     541          64 :         policies = lappend(policies, &pols[ii]);
     542             : 
     543          32 :     return policies;
     544             : }
     545             : 
     546             : /*
     547             :  * qsort comparator to sort RowSecurityPolicy entries by name
     548             :  */
     549             : static int
     550          32 : row_security_policy_cmp(const void *a, const void *b)
     551             : {
     552          32 :     const RowSecurityPolicy *pa = (const RowSecurityPolicy *) a;
     553          32 :     const RowSecurityPolicy *pb = (const RowSecurityPolicy *) b;
     554             : 
     555             :     /* Guard against NULL policy names from extensions */
     556          32 :     if (pa->policy_name == NULL)
     557           0 :         return pb->policy_name == NULL ? 0 : 1;
     558          32 :     if (pb->policy_name == NULL)
     559           0 :         return -1;
     560             : 
     561          32 :     return strcmp(pa->policy_name, pb->policy_name);
     562             : }
     563             : 
     564             : /*
     565             :  * add_security_quals
     566             :  *
     567             :  * Add security quals to enforce the specified RLS policies, restricting
     568             :  * access to existing data in a table.  If there are no policies controlling
     569             :  * access to the table, then all access is prohibited --- i.e., an implicit
     570             :  * default-deny policy is used.
     571             :  *
     572             :  * New security quals are added to securityQuals, and hasSubLinks is set to
     573             :  * true if any of the quals added contain sublink subqueries.
     574             :  */
     575             : static void
     576        1320 : add_security_quals(int rt_index,
     577             :                    List *permissive_policies,
     578             :                    List *restrictive_policies,
     579             :                    List **securityQuals,
     580             :                    bool *hasSubLinks)
     581             : {
     582             :     ListCell   *item;
     583        1320 :     List       *permissive_quals = NIL;
     584             :     Expr       *rowsec_expr;
     585             : 
     586             :     /*
     587             :      * First collect up the permissive quals.  If we do not find any
     588             :      * permissive policies then no rows are visible (this is handled below).
     589             :      */
     590        2728 :     foreach(item, permissive_policies)
     591             :     {
     592        1408 :         RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     593             : 
     594        1408 :         if (policy->qual != NULL)
     595             :         {
     596        1408 :             permissive_quals = lappend(permissive_quals,
     597        1408 :                                        copyObject(policy->qual));
     598        1408 :             *hasSubLinks |= policy->hassublinks;
     599             :         }
     600             :     }
     601             : 
     602             :     /*
     603             :      * We must have permissive quals, always, or no rows are visible.
     604             :      *
     605             :      * If we do not, then we simply return a single 'false' qual which results
     606             :      * in no rows being visible.
     607             :      */
     608        1320 :     if (permissive_quals != NIL)
     609             :     {
     610             :         /*
     611             :          * We now know that permissive policies exist, so we can now add
     612             :          * security quals based on the USING clauses from the restrictive
     613             :          * policies.  Since these need to be combined together using AND, we
     614             :          * can just add them one at a time.
     615             :          */
     616        1392 :         foreach(item, restrictive_policies)
     617             :         {
     618          92 :             RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     619             :             Expr       *qual;
     620             : 
     621          92 :             if (policy->qual != NULL)
     622             :             {
     623          92 :                 qual = copyObject(policy->qual);
     624          92 :                 ChangeVarNodes((Node *) qual, 1, rt_index, 0);
     625             : 
     626          92 :                 *securityQuals = list_append_unique(*securityQuals, qual);
     627          92 :                 *hasSubLinks |= policy->hassublinks;
     628             :             }
     629             :         }
     630             : 
     631             :         /*
     632             :          * Then add a single security qual combining together the USING
     633             :          * clauses from all the permissive policies using OR.
     634             :          */
     635        1300 :         if (list_length(permissive_quals) == 1)
     636        1224 :             rowsec_expr = (Expr *) linitial(permissive_quals);
     637             :         else
     638          76 :             rowsec_expr = makeBoolExpr(OR_EXPR, permissive_quals, -1);
     639             : 
     640        1300 :         ChangeVarNodes((Node *) rowsec_expr, 1, rt_index, 0);
     641        1300 :         *securityQuals = list_append_unique(*securityQuals, rowsec_expr);
     642             :     }
     643             :     else
     644             : 
     645             :         /*
     646             :          * A permissive policy must exist for rows to be visible at all.
     647             :          * Therefore, if there were no permissive policies found, return a
     648             :          * single always-false clause.
     649             :          */
     650          20 :         *securityQuals = lappend(*securityQuals,
     651          20 :                                  makeConst(BOOLOID, -1, InvalidOid,
     652             :                                            sizeof(bool), BoolGetDatum(false),
     653             :                                            false, true));
     654        1320 : }
     655             : 
     656             : /*
     657             :  * add_with_check_options
     658             :  *
     659             :  * Add WithCheckOptions of the specified kind to check that new records
     660             :  * added by an INSERT or UPDATE are consistent with the specified RLS
     661             :  * policies.  Normally new data must satisfy the WITH CHECK clauses from the
     662             :  * policies.  If a policy has no explicit WITH CHECK clause, its USING clause
     663             :  * is used instead.  In the special case of an UPDATE arising from an
     664             :  * INSERT ... ON CONFLICT DO UPDATE, existing records are first checked using
     665             :  * a WCO_RLS_CONFLICT_CHECK WithCheckOption, which always uses the USING
     666             :  * clauses from RLS policies.
     667             :  *
     668             :  * New WCOs are added to withCheckOptions, and hasSubLinks is set to true if
     669             :  * any of the check clauses added contain sublink subqueries.
     670             :  */
     671             : static void
     672         836 : add_with_check_options(Relation rel,
     673             :                        int rt_index,
     674             :                        WCOKind kind,
     675             :                        List *permissive_policies,
     676             :                        List *restrictive_policies,
     677             :                        List **withCheckOptions,
     678             :                        bool *hasSubLinks,
     679             :                        bool force_using)
     680             : {
     681             :     ListCell   *item;
     682         836 :     List       *permissive_quals = NIL;
     683             : 
     684             : #define QUAL_FOR_WCO(policy) \
     685             :     ( !force_using && \
     686             :       (policy)->with_check_qual != NULL ? \
     687             :       (policy)->with_check_qual : (policy)->qual )
     688             : 
     689             :     /*
     690             :      * First collect up the permissive policy clauses, similar to
     691             :      * add_security_quals.
     692             :      */
     693        1670 :     foreach(item, permissive_policies)
     694             :     {
     695         834 :         RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     696         834 :         Expr       *qual = QUAL_FOR_WCO(policy);
     697             : 
     698         834 :         if (qual != NULL)
     699             :         {
     700         834 :             permissive_quals = lappend(permissive_quals, copyObject(qual));
     701         834 :             *hasSubLinks |= policy->hassublinks;
     702             :         }
     703             :     }
     704             : 
     705             :     /*
     706             :      * There must be at least one permissive qual found or no rows are allowed
     707             :      * to be added.  This is the same as in add_security_quals.
     708             :      *
     709             :      * If there are no permissive_quals then we fall through and return a
     710             :      * single 'false' WCO, preventing all new rows.
     711             :      */
     712         836 :     if (permissive_quals != NIL)
     713             :     {
     714             :         /*
     715             :          * Add a single WithCheckOption for all the permissive policy clauses,
     716             :          * combining them together using OR.  This check has no policy name,
     717             :          * since if the check fails it means that no policy granted permission
     718             :          * to perform the update, rather than any particular policy being
     719             :          * violated.
     720             :          */
     721             :         WithCheckOption *wco;
     722             : 
     723         800 :         wco = makeNode(WithCheckOption);
     724         800 :         wco->kind = kind;
     725         800 :         wco->relname = pstrdup(RelationGetRelationName(rel));
     726         800 :         wco->polname = NULL;
     727         800 :         wco->cascaded = false;
     728             : 
     729         800 :         if (list_length(permissive_quals) == 1)
     730         766 :             wco->qual = (Node *) linitial(permissive_quals);
     731             :         else
     732          34 :             wco->qual = (Node *) makeBoolExpr(OR_EXPR, permissive_quals, -1);
     733             : 
     734         800 :         ChangeVarNodes(wco->qual, 1, rt_index, 0);
     735             : 
     736         800 :         *withCheckOptions = list_append_unique(*withCheckOptions, wco);
     737             : 
     738             :         /*
     739             :          * Now add WithCheckOptions for each of the restrictive policy clauses
     740             :          * (which will be combined together using AND).  We use a separate
     741             :          * WithCheckOption for each restrictive policy to allow the policy
     742             :          * name to be included in error reports if the policy is violated.
     743             :          */
     744         882 :         foreach(item, restrictive_policies)
     745             :         {
     746          82 :             RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     747          82 :             Expr       *qual = QUAL_FOR_WCO(policy);
     748             :             WithCheckOption *wco;
     749             : 
     750          82 :             if (qual != NULL)
     751             :             {
     752          82 :                 qual = copyObject(qual);
     753          82 :                 ChangeVarNodes((Node *) qual, 1, rt_index, 0);
     754             : 
     755          82 :                 wco = makeNode(WithCheckOption);
     756          82 :                 wco->kind = kind;
     757          82 :                 wco->relname = pstrdup(RelationGetRelationName(rel));
     758          82 :                 wco->polname = pstrdup(policy->policy_name);
     759          82 :                 wco->qual = (Node *) qual;
     760          82 :                 wco->cascaded = false;
     761             : 
     762          82 :                 *withCheckOptions = list_append_unique(*withCheckOptions, wco);
     763          82 :                 *hasSubLinks |= policy->hassublinks;
     764             :             }
     765             :         }
     766             :     }
     767             :     else
     768             :     {
     769             :         /*
     770             :          * If there were no policy clauses to check new data, add a single
     771             :          * always-false WCO (a default-deny policy).
     772             :          */
     773             :         WithCheckOption *wco;
     774             : 
     775          36 :         wco = makeNode(WithCheckOption);
     776          36 :         wco->kind = kind;
     777          36 :         wco->relname = pstrdup(RelationGetRelationName(rel));
     778          36 :         wco->polname = NULL;
     779          36 :         wco->qual = (Node *) makeConst(BOOLOID, -1, InvalidOid,
     780             :                                        sizeof(bool), BoolGetDatum(false),
     781             :                                        false, true);
     782          36 :         wco->cascaded = false;
     783             : 
     784          36 :         *withCheckOptions = lappend(*withCheckOptions, wco);
     785             :     }
     786         836 : }
     787             : 
     788             : /*
     789             :  * check_role_for_policy -
     790             :  *   determines if the policy should be applied for the current role
     791             :  */
     792             : static bool
     793        2676 : check_role_for_policy(ArrayType *policy_roles, Oid user_id)
     794             : {
     795             :     int         i;
     796        2676 :     Oid        *roles = (Oid *) ARR_DATA_PTR(policy_roles);
     797             : 
     798             :     /* Quick fall-thru for policies applied to all roles */
     799        2676 :     if (roles[0] == ACL_ID_PUBLIC)
     800        1908 :         return true;
     801             : 
     802        1332 :     for (i = 0; i < ARR_DIMS(policy_roles)[0]; i++)
     803             :     {
     804         768 :         if (has_privs_of_role(user_id, roles[i]))
     805         204 :             return true;
     806             :     }
     807             : 
     808         564 :     return false;
     809             : }

Generated by: LCOV version 1.13