LCOV - code coverage report
Current view: top level - src/backend/utils/misc - rls.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 31 32 96.9 %
Date: 2024-04-23 06:11:44 Functions: 3 3 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * rls.c
       4             :  *        RLS-related utility functions.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2024, 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/fmgrprotos.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      858106 : check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
      53             : {
      54      858106 :     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      858106 :     if (relid < (Oid) FirstNormalObjectId)
      63      221978 :         return RLS_NONE;
      64             : 
      65             :     /* Fetch relation's relrowsecurity and relforcerowsecurity flags */
      66      636128 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
      67      636128 :     if (!HeapTupleIsValid(tuple))
      68           0 :         return RLS_NONE;
      69      636128 :     classform = (Form_pg_class) GETSTRUCT(tuple);
      70             : 
      71      636128 :     relrowsecurity = classform->relrowsecurity;
      72      636128 :     relforcerowsecurity = classform->relforcerowsecurity;
      73             : 
      74      636128 :     ReleaseSysCache(tuple);
      75             : 
      76             :     /* Nothing to do if the relation does not have RLS */
      77      636128 :     if (!relrowsecurity)
      78      632626 :         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        3502 :     if (has_bypassrls_privilege(user_id))
      88         518 :         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        2984 :     amowner = object_ownercheck(RelationRelationId, relid, user_id);
      99        2984 :     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         392 :         if (!relforcerowsecurity || InNoForceRLSOperation())
     117         230 :             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        2754 :     if (!row_security && !noError)
     125          66 :         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        2688 :     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          12 : row_security_active(PG_FUNCTION_ARGS)
     143             : {
     144             :     /* By OID */
     145          12 :     Oid         tableoid = PG_GETARG_OID(0);
     146             :     int         rls_status;
     147             : 
     148          12 :     rls_status = check_enable_rls(tableoid, InvalidOid, true);
     149          12 :     PG_RETURN_BOOL(rls_status == RLS_ENABLED);
     150             : }
     151             : 
     152             : Datum
     153          12 : row_security_active_name(PG_FUNCTION_ARGS)
     154             : {
     155             :     /* By qualified name */
     156          12 :     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          12 :     tablerel = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
     163          12 :     tableoid = RangeVarGetRelid(tablerel, NoLock, false);
     164             : 
     165          12 :     rls_status = check_enable_rls(tableoid, InvalidOid, true);
     166          12 :     PG_RETURN_BOOL(rls_status == RLS_ENABLED);
     167             : }

Generated by: LCOV version 1.14