LCOV - code coverage report
Current view: top level - src/backend/rewrite - rowsecurity.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 176 180 97.8 %
Date: 2019-08-24 15:07:19 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-2019, 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 "utils/acl.h"
      55             : #include "utils/lsyscache.h"
      56             : #include "utils/rel.h"
      57             : #include "utils/rls.h"
      58             : #include "utils/syscache.h"
      59             : #include "tcop/utility.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      276122 : 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      276122 :     *securityQuals = NIL;
     121      276122 :     *withCheckOptions = NIL;
     122      276122 :     *hasRowSecurity = false;
     123      276122 :     *hasSubLinks = false;
     124             : 
     125             :     /* If this is not a normal relation, just return immediately */
     126      281088 :     if (rte->relkind != RELKIND_RELATION &&
     127        4966 :         rte->relkind != RELKIND_PARTITIONED_TABLE)
     128           0 :         return;
     129             : 
     130             :     /* Switch to checkAsUser if it's set */
     131      276122 :     user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
     132             : 
     133             :     /* Determine the state of RLS for this, pass checkAsUser explicitly */
     134      276122 :     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      276090 :     if (rls_status == RLS_NONE)
     138      274468 :         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        1622 :     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         310 :         *hasRowSecurity = true;
     153             : 
     154         310 :         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        1312 :     rel = table_open(rte->relid, NoLock);
     167             : 
     168        2624 :     commandType = rt_index == root->resultRelation ?
     169        1312 :         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        1312 :     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        1312 :     get_policies_for_relation(rel, commandType, user_id, &permissive_policies,
     223             :                               &restrictive_policies);
     224             : 
     225        1312 :     if (commandType == CMD_SELECT ||
     226         244 :         commandType == CMD_UPDATE ||
     227             :         commandType == CMD_DELETE)
     228        1124 :         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        1528 :     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        1312 :     if (commandType == CMD_INSERT || commandType == CMD_UPDATE)
     267             :     {
     268             :         /* This should be the target relation */
     269             :         Assert(rt_index == root->resultRelation);
     270             : 
     271         348 :         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         348 :         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         536 :         if (commandType == CMD_INSERT &&
     310         252 :             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        1312 :     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        1312 :     setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser);
     390        1312 :     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        1312 :     *hasRowSecurity = true;
     397             : 
     398        1312 :     return;
     399             : }
     400             : 
     401             : /*
     402             :  * get_policies_for_relation
     403             :  *
     404             :  * Returns lists of permissive and restrictive policies to be applied to the
     405             :  * specified relation, based on the command type and role.
     406             :  *
     407             :  * This includes any policies added by extensions.
     408             :  */
     409             : static void
     410        1892 : get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id,
     411             :                           List **permissive_policies,
     412             :                           List **restrictive_policies)
     413             : {
     414             :     ListCell   *item;
     415             : 
     416        1892 :     *permissive_policies = NIL;
     417        1892 :     *restrictive_policies = NIL;
     418             : 
     419             :     /* First find all internal policies for the relation. */
     420        5490 :     foreach(item, relation->rd_rsdesc->policies)
     421             :     {
     422        3598 :         bool        cmd_matches = false;
     423        3598 :         RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     424             : 
     425             :         /* Always add ALL policies, if they exist. */
     426        3598 :         if (policy->polcmd == '*')
     427        2070 :             cmd_matches = true;
     428             :         else
     429             :         {
     430             :             /* Check whether the policy applies to the specified command type */
     431        1528 :             switch (cmd)
     432             :             {
     433             :                 case CMD_SELECT:
     434         944 :                     if (policy->polcmd == ACL_SELECT_CHR)
     435         312 :                         cmd_matches = true;
     436         944 :                     break;
     437             :                 case CMD_INSERT:
     438         188 :                     if (policy->polcmd == ACL_INSERT_CHR)
     439          64 :                         cmd_matches = true;
     440         188 :                     break;
     441             :                 case CMD_UPDATE:
     442         340 :                     if (policy->polcmd == ACL_UPDATE_CHR)
     443         140 :                         cmd_matches = true;
     444         340 :                     break;
     445             :                 case CMD_DELETE:
     446          56 :                     if (policy->polcmd == ACL_DELETE_CHR)
     447          16 :                         cmd_matches = true;
     448          56 :                     break;
     449             :                 default:
     450           0 :                     elog(ERROR, "unrecognized policy command type %d",
     451             :                          (int) cmd);
     452             :                     break;
     453             :             }
     454             :         }
     455             : 
     456             :         /*
     457             :          * Add this policy to the relevant list of policies if it applies to
     458             :          * the specified role.
     459             :          */
     460        3598 :         if (cmd_matches && check_role_for_policy(policy->roles, user_id))
     461             :         {
     462        2038 :             if (policy->permissive)
     463        1922 :                 *permissive_policies = lappend(*permissive_policies, policy);
     464             :             else
     465         116 :                 *restrictive_policies = lappend(*restrictive_policies, policy);
     466             :         }
     467             :     }
     468             : 
     469             :     /*
     470             :      * We sort restrictive policies by name so that any WCOs they generate are
     471             :      * checked in a well-defined order.
     472             :      */
     473        1892 :     sort_policies_by_name(*restrictive_policies);
     474             : 
     475             :     /*
     476             :      * Then add any permissive or restrictive policies defined by extensions.
     477             :      * These are simply appended to the lists of internal policies, if they
     478             :      * apply to the specified role.
     479             :      */
     480        1892 :     if (row_security_policy_hook_restrictive)
     481             :     {
     482          60 :         List       *hook_policies =
     483          60 :         (*row_security_policy_hook_restrictive) (cmd, relation);
     484             : 
     485             :         /*
     486             :          * As with built-in restrictive policies, we sort any hook-provided
     487             :          * restrictive policies by name also.  Note that we also intentionally
     488             :          * always check all built-in restrictive policies, in name order,
     489             :          * before checking restrictive policies added by hooks, in name order.
     490             :          */
     491          60 :         sort_policies_by_name(hook_policies);
     492             : 
     493         102 :         foreach(item, hook_policies)
     494             :         {
     495          42 :             RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     496             : 
     497          42 :             if (check_role_for_policy(policy->roles, user_id))
     498          42 :                 *restrictive_policies = lappend(*restrictive_policies, policy);
     499             :         }
     500             :     }
     501             : 
     502        1892 :     if (row_security_policy_hook_permissive)
     503             :     {
     504          60 :         List       *hook_policies =
     505          60 :         (*row_security_policy_hook_permissive) (cmd, relation);
     506             : 
     507         100 :         foreach(item, hook_policies)
     508             :         {
     509          40 :             RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     510             : 
     511          40 :             if (check_role_for_policy(policy->roles, user_id))
     512          40 :                 *permissive_policies = lappend(*permissive_policies, policy);
     513             :         }
     514             :     }
     515        1892 : }
     516             : 
     517             : /*
     518             :  * sort_policies_by_name
     519             :  *
     520             :  * This is only used for restrictive policies, ensuring that any
     521             :  * WithCheckOptions they generate are applied in a well-defined order.
     522             :  * This is not necessary for permissive policies, since they are all combined
     523             :  * together using OR into a single WithCheckOption check.
     524             :  */
     525             : static void
     526        1952 : sort_policies_by_name(List *policies)
     527             : {
     528        1952 :     list_sort(policies, row_security_policy_cmp);
     529        1952 : }
     530             : 
     531             : /*
     532             :  * list_sort comparator to sort RowSecurityPolicy entries by name
     533             :  */
     534             : static int
     535          32 : row_security_policy_cmp(const ListCell *a, const ListCell *b)
     536             : {
     537          32 :     const RowSecurityPolicy *pa = (const RowSecurityPolicy *) lfirst(a);
     538          32 :     const RowSecurityPolicy *pb = (const RowSecurityPolicy *) lfirst(b);
     539             : 
     540             :     /* Guard against NULL policy names from extensions */
     541          32 :     if (pa->policy_name == NULL)
     542           0 :         return pb->policy_name == NULL ? 0 : 1;
     543          32 :     if (pb->policy_name == NULL)
     544           0 :         return -1;
     545             : 
     546          32 :     return strcmp(pa->policy_name, pb->policy_name);
     547             : }
     548             : 
     549             : /*
     550             :  * add_security_quals
     551             :  *
     552             :  * Add security quals to enforce the specified RLS policies, restricting
     553             :  * access to existing data in a table.  If there are no policies controlling
     554             :  * access to the table, then all access is prohibited --- i.e., an implicit
     555             :  * default-deny policy is used.
     556             :  *
     557             :  * New security quals are added to securityQuals, and hasSubLinks is set to
     558             :  * true if any of the quals added contain sublink subqueries.
     559             :  */
     560             : static void
     561        1344 : add_security_quals(int rt_index,
     562             :                    List *permissive_policies,
     563             :                    List *restrictive_policies,
     564             :                    List **securityQuals,
     565             :                    bool *hasSubLinks)
     566             : {
     567             :     ListCell   *item;
     568        1344 :     List       *permissive_quals = NIL;
     569             :     Expr       *rowsec_expr;
     570             : 
     571             :     /*
     572             :      * First collect up the permissive quals.  If we do not find any
     573             :      * permissive policies then no rows are visible (this is handled below).
     574             :      */
     575        2760 :     foreach(item, permissive_policies)
     576             :     {
     577        1416 :         RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     578             : 
     579        1416 :         if (policy->qual != NULL)
     580             :         {
     581        1416 :             permissive_quals = lappend(permissive_quals,
     582        1416 :                                        copyObject(policy->qual));
     583        1416 :             *hasSubLinks |= policy->hassublinks;
     584             :         }
     585             :     }
     586             : 
     587             :     /*
     588             :      * We must have permissive quals, always, or no rows are visible.
     589             :      *
     590             :      * If we do not, then we simply return a single 'false' qual which results
     591             :      * in no rows being visible.
     592             :      */
     593        1344 :     if (permissive_quals != NIL)
     594             :     {
     595             :         /*
     596             :          * We now know that permissive policies exist, so we can now add
     597             :          * security quals based on the USING clauses from the restrictive
     598             :          * policies.  Since these need to be combined together using AND, we
     599             :          * can just add them one at a time.
     600             :          */
     601        1400 :         foreach(item, restrictive_policies)
     602             :         {
     603          92 :             RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     604             :             Expr       *qual;
     605             : 
     606          92 :             if (policy->qual != NULL)
     607             :             {
     608          92 :                 qual = copyObject(policy->qual);
     609          92 :                 ChangeVarNodes((Node *) qual, 1, rt_index, 0);
     610             : 
     611          92 :                 *securityQuals = list_append_unique(*securityQuals, qual);
     612          92 :                 *hasSubLinks |= policy->hassublinks;
     613             :             }
     614             :         }
     615             : 
     616             :         /*
     617             :          * Then add a single security qual combining together the USING
     618             :          * clauses from all the permissive policies using OR.
     619             :          */
     620        1308 :         if (list_length(permissive_quals) == 1)
     621        1232 :             rowsec_expr = (Expr *) linitial(permissive_quals);
     622             :         else
     623          76 :             rowsec_expr = makeBoolExpr(OR_EXPR, permissive_quals, -1);
     624             : 
     625        1308 :         ChangeVarNodes((Node *) rowsec_expr, 1, rt_index, 0);
     626        1308 :         *securityQuals = list_append_unique(*securityQuals, rowsec_expr);
     627             :     }
     628             :     else
     629             : 
     630             :         /*
     631             :          * A permissive policy must exist for rows to be visible at all.
     632             :          * Therefore, if there were no permissive policies found, return a
     633             :          * single always-false clause.
     634             :          */
     635          36 :         *securityQuals = lappend(*securityQuals,
     636          36 :                                  makeConst(BOOLOID, -1, InvalidOid,
     637             :                                            sizeof(bool), BoolGetDatum(false),
     638             :                                            false, true));
     639        1344 : }
     640             : 
     641             : /*
     642             :  * add_with_check_options
     643             :  *
     644             :  * Add WithCheckOptions of the specified kind to check that new records
     645             :  * added by an INSERT or UPDATE are consistent with the specified RLS
     646             :  * policies.  Normally new data must satisfy the WITH CHECK clauses from the
     647             :  * policies.  If a policy has no explicit WITH CHECK clause, its USING clause
     648             :  * is used instead.  In the special case of an UPDATE arising from an
     649             :  * INSERT ... ON CONFLICT DO UPDATE, existing records are first checked using
     650             :  * a WCO_RLS_CONFLICT_CHECK WithCheckOption, which always uses the USING
     651             :  * clauses from RLS policies.
     652             :  *
     653             :  * New WCOs are added to withCheckOptions, and hasSubLinks is set to true if
     654             :  * any of the check clauses added contain sublink subqueries.
     655             :  */
     656             : static void
     657         836 : add_with_check_options(Relation rel,
     658             :                        int rt_index,
     659             :                        WCOKind kind,
     660             :                        List *permissive_policies,
     661             :                        List *restrictive_policies,
     662             :                        List **withCheckOptions,
     663             :                        bool *hasSubLinks,
     664             :                        bool force_using)
     665             : {
     666             :     ListCell   *item;
     667         836 :     List       *permissive_quals = NIL;
     668             : 
     669             : #define QUAL_FOR_WCO(policy) \
     670             :     ( !force_using && \
     671             :       (policy)->with_check_qual != NULL ? \
     672             :       (policy)->with_check_qual : (policy)->qual )
     673             : 
     674             :     /*
     675             :      * First collect up the permissive policy clauses, similar to
     676             :      * add_security_quals.
     677             :      */
     678        1670 :     foreach(item, permissive_policies)
     679             :     {
     680         834 :         RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     681         834 :         Expr       *qual = QUAL_FOR_WCO(policy);
     682             : 
     683         834 :         if (qual != NULL)
     684             :         {
     685         834 :             permissive_quals = lappend(permissive_quals, copyObject(qual));
     686         834 :             *hasSubLinks |= policy->hassublinks;
     687             :         }
     688             :     }
     689             : 
     690             :     /*
     691             :      * There must be at least one permissive qual found or no rows are allowed
     692             :      * to be added.  This is the same as in add_security_quals.
     693             :      *
     694             :      * If there are no permissive_quals then we fall through and return a
     695             :      * single 'false' WCO, preventing all new rows.
     696             :      */
     697         836 :     if (permissive_quals != NIL)
     698             :     {
     699             :         /*
     700             :          * Add a single WithCheckOption for all the permissive policy clauses,
     701             :          * combining them together using OR.  This check has no policy name,
     702             :          * since if the check fails it means that no policy granted permission
     703             :          * to perform the update, rather than any particular policy being
     704             :          * violated.
     705             :          */
     706             :         WithCheckOption *wco;
     707             : 
     708         800 :         wco = makeNode(WithCheckOption);
     709         800 :         wco->kind = kind;
     710         800 :         wco->relname = pstrdup(RelationGetRelationName(rel));
     711         800 :         wco->polname = NULL;
     712         800 :         wco->cascaded = false;
     713             : 
     714         800 :         if (list_length(permissive_quals) == 1)
     715         766 :             wco->qual = (Node *) linitial(permissive_quals);
     716             :         else
     717          34 :             wco->qual = (Node *) makeBoolExpr(OR_EXPR, permissive_quals, -1);
     718             : 
     719         800 :         ChangeVarNodes(wco->qual, 1, rt_index, 0);
     720             : 
     721         800 :         *withCheckOptions = list_append_unique(*withCheckOptions, wco);
     722             : 
     723             :         /*
     724             :          * Now add WithCheckOptions for each of the restrictive policy clauses
     725             :          * (which will be combined together using AND).  We use a separate
     726             :          * WithCheckOption for each restrictive policy to allow the policy
     727             :          * name to be included in error reports if the policy is violated.
     728             :          */
     729         882 :         foreach(item, restrictive_policies)
     730             :         {
     731          82 :             RowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(item);
     732          82 :             Expr       *qual = QUAL_FOR_WCO(policy);
     733             :             WithCheckOption *wco;
     734             : 
     735          82 :             if (qual != NULL)
     736             :             {
     737          82 :                 qual = copyObject(qual);
     738          82 :                 ChangeVarNodes((Node *) qual, 1, rt_index, 0);
     739             : 
     740          82 :                 wco = makeNode(WithCheckOption);
     741          82 :                 wco->kind = kind;
     742          82 :                 wco->relname = pstrdup(RelationGetRelationName(rel));
     743          82 :                 wco->polname = pstrdup(policy->policy_name);
     744          82 :                 wco->qual = (Node *) qual;
     745          82 :                 wco->cascaded = false;
     746             : 
     747          82 :                 *withCheckOptions = list_append_unique(*withCheckOptions, wco);
     748          82 :                 *hasSubLinks |= policy->hassublinks;
     749             :             }
     750             :         }
     751             :     }
     752             :     else
     753             :     {
     754             :         /*
     755             :          * If there were no policy clauses to check new data, add a single
     756             :          * always-false WCO (a default-deny policy).
     757             :          */
     758             :         WithCheckOption *wco;
     759             : 
     760          36 :         wco = makeNode(WithCheckOption);
     761          36 :         wco->kind = kind;
     762          36 :         wco->relname = pstrdup(RelationGetRelationName(rel));
     763          36 :         wco->polname = NULL;
     764          36 :         wco->qual = (Node *) makeConst(BOOLOID, -1, InvalidOid,
     765             :                                        sizeof(bool), BoolGetDatum(false),
     766             :                                        false, true);
     767          36 :         wco->cascaded = false;
     768             : 
     769          36 :         *withCheckOptions = lappend(*withCheckOptions, wco);
     770             :     }
     771         836 : }
     772             : 
     773             : /*
     774             :  * check_role_for_policy -
     775             :  *   determines if the policy should be applied for the current role
     776             :  */
     777             : static bool
     778        2684 : check_role_for_policy(ArrayType *policy_roles, Oid user_id)
     779             : {
     780             :     int         i;
     781        2684 :     Oid        *roles = (Oid *) ARR_DATA_PTR(policy_roles);
     782             : 
     783             :     /* Quick fall-thru for policies applied to all roles */
     784        2684 :     if (roles[0] == ACL_ID_PUBLIC)
     785        1916 :         return true;
     786             : 
     787        1332 :     for (i = 0; i < ARR_DIMS(policy_roles)[0]; i++)
     788             :     {
     789         768 :         if (has_privs_of_role(user_id, roles[i]))
     790         204 :             return true;
     791             :     }
     792             : 
     793         564 :     return false;
     794             : }

Generated by: LCOV version 1.13