1/*-------------------------------------------------------------------------
2 *
3 * rls.c
4 * RLS-related utility functions.
5 *
6 * Portions Copyright (c) 1996-2019, 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 */
51int
52check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
53{
54 Oid user_id = 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 = pg_class_ownercheck(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 */
141Datum
142row_security_active(PG_FUNCTION_ARGS)
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
152Datum
153row_security_active_name(PG_FUNCTION_ARGS)
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}
168