Age Owner Branch data TLA 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
3380 sfrost@snowman.net 52 :CBC 425848 : check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
53 : : {
501 alvherre@alvh.no-ip. 54 [ + + ]: 425848 : 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 */
3023 tgl@sss.pgh.pa.us 62 [ + + ]: 425848 : if (relid < (Oid) FirstNormalObjectId)
3183 mail@joeconway.com 63 : 109500 : return RLS_NONE;
64 : :
65 : : /* Fetch relation's relrowsecurity and relforcerowsecurity flags */
3380 sfrost@snowman.net 66 : 316348 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
67 [ - + ]: 316348 : if (!HeapTupleIsValid(tuple))
3380 sfrost@snowman.net 68 :UBC 0 : return RLS_NONE;
3380 sfrost@snowman.net 69 :CBC 316348 : classform = (Form_pg_class) GETSTRUCT(tuple);
70 : :
71 : 316348 : relrowsecurity = classform->relrowsecurity;
3115 72 : 316348 : relforcerowsecurity = classform->relforcerowsecurity;
73 : :
3380 74 : 316348 : ReleaseSysCache(tuple);
75 : :
76 : : /* Nothing to do if the relation does not have RLS */
77 [ + + ]: 316348 : if (!relrowsecurity)
78 : 314593 : 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 : : */
3115 87 [ + + ]: 1755 : if (has_bypassrls_privilege(user_id))
3380 88 : 262 : 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 : : */
518 peter@eisentraut.org 98 : 1493 : amowner = object_ownercheck(RelationRelationId, relid, user_id);
3023 tgl@sss.pgh.pa.us 99 [ + + ]: 1493 : 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 [ + + + + ]: 196 : if (!relforcerowsecurity || InNoForceRLSOperation())
3115 sfrost@snowman.net 117 : 115 : 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 : : */
3116 noah@leadboat.com 124 [ + + + - ]: 1378 : 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. */
3380 sfrost@snowman.net 132 : 1345 : 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
3183 mail@joeconway.com 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 */
2590 noah@leadboat.com 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. */
3183 mail@joeconway.com 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 : : }
|