]> granicus.if.org Git - postgresql/blob - src/backend/utils/misc/rls.c
Update copyright via script for 2017
[postgresql] / src / backend / utils / misc / rls.c
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
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 = 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  */
141 Datum
142 row_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
152 Datum
153 row_security_active_name(PG_FUNCTION_ARGS)
154 {
155         /* By qualified name */
156         text       *tablename = PG_GETARG_TEXT_P(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 }