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

Generated by: LCOV version 1.13