LCOV - differential code coverage report
Current view: top level - src/backend/utils/misc - rls.c (source / functions) Coverage Total Hit UBC GNC CBC DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 96.9 % 32 31 1 2 29 2
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 3 3 1 2
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * rls.c
       4                 :  *        RLS-related utility functions.
       5                 :  *
       6                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       7                 :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :  *
       9                 :  *
      10                 :  * IDENTIFICATION
      11                 :  *        src/backend/utils/misc/rls.c
      12                 :  *
      13                 :  *-------------------------------------------------------------------------
      14                 : */
      15                 : #include "postgres.h"
      16                 : 
      17                 : #include "access/htup.h"
      18                 : #include "access/htup_details.h"
      19                 : #include "access/transam.h"
      20                 : #include "catalog/namespace.h"
      21                 : #include "catalog/pg_class.h"
      22                 : #include "miscadmin.h"
      23                 : #include "utils/acl.h"
      24                 : #include "utils/builtins.h"
      25                 : #include "utils/lsyscache.h"
      26                 : #include "utils/rls.h"
      27                 : #include "utils/syscache.h"
      28                 : #include "utils/varlena.h"
      29                 : 
      30                 : 
      31                 : /*
      32                 :  * check_enable_rls
      33                 :  *
      34                 :  * Determine, based on the relation, row_security setting, and current role,
      35                 :  * if RLS is applicable to this query.  RLS_NONE_ENV indicates that, while
      36                 :  * RLS is not to be added for this query, a change in the environment may change
      37                 :  * that.  RLS_NONE means that RLS is not on the relation at all and therefore
      38                 :  * we don't need to worry about it.  RLS_ENABLED means RLS should be implemented
      39                 :  * for the table and the plan cache needs to be invalidated if the environment
      40                 :  * changes.
      41                 :  *
      42                 :  * Handle checking as another role via checkAsUser (for views, etc).  Pass
      43                 :  * InvalidOid to check the current user.
      44                 :  *
      45                 :  * If noError is set to 'true' then we just return RLS_ENABLED instead of doing
      46                 :  * an ereport() if the user has attempted to bypass RLS and they are not
      47                 :  * allowed to.  This allows users to check if RLS is enabled without having to
      48                 :  * deal with the actual error case (eg: error cases which are trying to decide
      49                 :  * if the user should get data from the relation back as part of the error).
      50                 :  */
      51                 : int
      52 CBC      421562 : check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
      53                 : {
      54 GNC      421562 :     Oid         user_id = OidIsValid(checkAsUser) ? checkAsUser : GetUserId();
      55                 :     HeapTuple   tuple;
      56                 :     Form_pg_class classform;
      57                 :     bool        relrowsecurity;
      58                 :     bool        relforcerowsecurity;
      59                 :     bool        amowner;
      60                 : 
      61                 :     /* Nothing to do for built-in relations */
      62 CBC      421562 :     if (relid < (Oid) FirstNormalObjectId)
      63          116837 :         return RLS_NONE;
      64                 : 
      65                 :     /* Fetch relation's relrowsecurity and relforcerowsecurity flags */
      66          304725 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
      67          304725 :     if (!HeapTupleIsValid(tuple))
      68 UBC           0 :         return RLS_NONE;
      69 CBC      304725 :     classform = (Form_pg_class) GETSTRUCT(tuple);
      70                 : 
      71          304725 :     relrowsecurity = classform->relrowsecurity;
      72          304725 :     relforcerowsecurity = classform->relforcerowsecurity;
      73                 : 
      74          304725 :     ReleaseSysCache(tuple);
      75                 : 
      76                 :     /* Nothing to do if the relation does not have RLS */
      77          304725 :     if (!relrowsecurity)
      78          303093 :         return RLS_NONE;
      79                 : 
      80                 :     /*
      81                 :      * BYPASSRLS users always bypass RLS.  Note that superusers are always
      82                 :      * considered to have BYPASSRLS.
      83                 :      *
      84                 :      * Return RLS_NONE_ENV to indicate that this decision depends on the
      85                 :      * environment (in this case, the user_id).
      86                 :      */
      87            1632 :     if (has_bypassrls_privilege(user_id))
      88             252 :         return RLS_NONE_ENV;
      89                 : 
      90                 :     /*
      91                 :      * Table owners generally bypass RLS, except if the table has been set (by
      92                 :      * an owner) to FORCE ROW SECURITY, and this is not a referential
      93                 :      * integrity check.
      94                 :      *
      95                 :      * Return RLS_NONE_ENV to indicate that this decision depends on the
      96                 :      * environment (in this case, the user_id).
      97                 :      */
      98 GNC        1380 :     amowner = object_ownercheck(RelationRelationId, relid, user_id);
      99 CBC        1380 :     if (amowner)
     100                 :     {
     101                 :         /*
     102                 :          * If FORCE ROW LEVEL SECURITY has been set on the relation then we
     103                 :          * should return RLS_ENABLED to indicate that RLS should be applied.
     104                 :          * If not, or if we are in an InNoForceRLSOperation context, we return
     105                 :          * RLS_NONE_ENV.
     106                 :          *
     107                 :          * InNoForceRLSOperation indicates that we should not apply RLS even
     108                 :          * if the table has FORCE RLS set - IF the current user is the owner.
     109                 :          * This is specifically to ensure that referential integrity checks
     110                 :          * are able to still run correctly.
     111                 :          *
     112                 :          * This is intentionally only done after we have checked that the user
     113                 :          * is the table owner, which should always be the case for referential
     114                 :          * integrity checks.
     115                 :          */
     116             195 :         if (!relforcerowsecurity || InNoForceRLSOperation())
     117             114 :             return RLS_NONE_ENV;
     118                 :     }
     119                 : 
     120                 :     /*
     121                 :      * We should apply RLS.  However, the user may turn off the row_security
     122                 :      * GUC to get a forced error instead.
     123                 :      */
     124            1266 :     if (!row_security && !noError)
     125              33 :         ereport(ERROR,
     126                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     127                 :                  errmsg("query would be affected by row-level security policy for table \"%s\"",
     128                 :                         get_rel_name(relid)),
     129                 :                  amowner ? errhint("To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.") : 0));
     130                 : 
     131                 :     /* RLS should be fully enabled for this relation. */
     132            1233 :     return RLS_ENABLED;
     133                 : }
     134                 : 
     135                 : /*
     136                 :  * row_security_active
     137                 :  *
     138                 :  * check_enable_rls wrapped as a SQL callable function except
     139                 :  * RLS_NONE_ENV and RLS_NONE are the same for this purpose.
     140                 :  */
     141                 : Datum
     142               6 : row_security_active(PG_FUNCTION_ARGS)
     143                 : {
     144                 :     /* By OID */
     145               6 :     Oid         tableoid = PG_GETARG_OID(0);
     146                 :     int         rls_status;
     147                 : 
     148               6 :     rls_status = check_enable_rls(tableoid, InvalidOid, true);
     149               6 :     PG_RETURN_BOOL(rls_status == RLS_ENABLED);
     150                 : }
     151                 : 
     152                 : Datum
     153               6 : row_security_active_name(PG_FUNCTION_ARGS)
     154                 : {
     155                 :     /* By qualified name */
     156               6 :     text       *tablename = PG_GETARG_TEXT_PP(0);
     157                 :     RangeVar   *tablerel;
     158                 :     Oid         tableoid;
     159                 :     int         rls_status;
     160                 : 
     161                 :     /* Look up table name.  Can't lock it - we might not have privileges. */
     162               6 :     tablerel = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
     163               6 :     tableoid = RangeVarGetRelid(tablerel, NoLock, false);
     164                 : 
     165               6 :     rls_status = check_enable_rls(tableoid, InvalidOid, true);
     166               6 :     PG_RETURN_BOOL(rls_status == RLS_ENABLED);
     167                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a