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