PostgreSQL Source Code  git master
rls.c
Go to the documentation of this file.
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 check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
53 {
54  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  if (relid < (Oid) FirstNormalObjectId)
63  return RLS_NONE;
64 
65  /* Fetch relation's relrowsecurity and relforcerowsecurity flags */
66  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
67  if (!HeapTupleIsValid(tuple))
68  return RLS_NONE;
69  classform = (Form_pg_class) GETSTRUCT(tuple);
70 
71  relrowsecurity = classform->relrowsecurity;
72  relforcerowsecurity = classform->relforcerowsecurity;
73 
74  ReleaseSysCache(tuple);
75 
76  /* Nothing to do if the relation does not have RLS */
77  if (!relrowsecurity)
78  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  if (has_bypassrls_privilege(user_id))
88  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  amowner = object_ownercheck(RelationRelationId, relid, user_id);
99  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  if (!relforcerowsecurity || InNoForceRLSOperation())
117  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  if (!row_security && !noError)
125  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  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
143 {
144  /* By OID */
145  Oid tableoid = PG_GETARG_OID(0);
146  int rls_status;
147 
148  rls_status = check_enable_rls(tableoid, InvalidOid, true);
149  PG_RETURN_BOOL(rls_status == RLS_ENABLED);
150 }
151 
152 Datum
154 {
155  /* By qualified name */
156  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  tablerel = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
163  tableoid = RangeVarGetRelid(tablerel, NoLock, false);
164 
165  rls_status = check_enable_rls(tableoid, InvalidOid, true);
166  PG_RETURN_BOOL(rls_status == RLS_ENABLED);
167 }
bool has_bypassrls_privilege(Oid roleid)
Definition: aclchk.c:4230
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4130
#define OidIsValid(objectId)
Definition: c.h:775
int errhint(const char *fmt,...)
Definition: elog.c:1319
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
bool row_security
Definition: guc_tables.c:513
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
#define NoLock
Definition: lockdefs.h:34
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1928
Oid GetUserId(void)
Definition: miscinit.c:514
bool InNoForceRLSOperation(void)
Definition: miscinit.c:671
RangeVar * makeRangeVarFromNameList(const List *names)
Definition: namespace.c:3539
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition: namespace.h:80
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
uintptr_t Datum
Definition: postgres.h:64
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
Datum row_security_active(PG_FUNCTION_ARGS)
Definition: rls.c:142
int check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
Definition: rls.c:52
Datum row_security_active_name(PG_FUNCTION_ARGS)
Definition: rls.c:153
@ RLS_NONE
Definition: rls.h:43
@ RLS_NONE_ENV
Definition: rls.h:44
@ RLS_ENABLED
Definition: rls.h:45
Definition: c.h:687
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218
#define FirstNormalObjectId
Definition: transam.h:197
List * textToQualifiedNameList(text *textval)
Definition: varlena.c:3399