Commit 3cb0a7e7 authored by Noah Misch's avatar Noah Misch

Make BYPASSRLS behave like superuser RLS bypass.

Specifically, make its effect independent from the row_security GUC, and
make it affect permission checks pertinent to views the BYPASSRLS role
owns.  The row_security GUC thereby ceases to change successful-query
behavior; it can only make a query fail with an error.  Back-patch to
9.5, where BYPASSRLS was introduced.
parent 23fc0b48
......@@ -1454,7 +1454,7 @@
<entry><structfield>rolbypassrls</structfield></entry>
<entry><type>bool</type></entry>
<entry>
Role can bypass row level security policies, see
Role bypasses every row level security policy, see
<xref linkend="ddl-rowsecurity"> for more information.
</entry>
</row>
......@@ -9413,7 +9413,7 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
<entry><type>bool</type></entry>
<entry></entry>
<entry>
User can bypass row level security policies, see
User bypasses every row level security policy, see
<xref linkend="ddl-rowsecurity"> for more information.
</entry>
</row>
......@@ -9888,7 +9888,7 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
<entry><structfield>usebypassrls</structfield></entry>
<entry><type>bool</type></entry>
<entry>
User can bypass row level security policies, see
User bypasses every row level security policy, see
<xref linkend="ddl-rowsecurity"> for more information.
</entry>
</row>
......
......@@ -5591,22 +5591,15 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</term>
<listitem>
<para>
This variable controls if row security policies are to be applied
to queries which are run against tables that have row security enabled.
The default is <literal>on</>. When set to <literal>on</>, all users,
except superusers and the owner of the table, will have the row
policies for the table applied to their queries. When set to
<literal>off</>, queries will bypass row policies for the table, if
possible, and error if not.
</para>
<para>
For a user who is not a superuser and not the table owner to bypass
row policies for the table, they must have the <literal>BYPASSRLS</>
role attribute. If this is set to <literal>off</> and the user queries
a table which has row policies enabled and the user does not have the
right to bypass row policies then a permission denied error will be
returned.
This variable controls whether to raise an error in lieu of applying a
row security policy. When set to <literal>on</>, policies apply
normally. When set to <literal>off</>, queries fail which would
otherwise apply at least one policy. The default is <literal>on</>.
Change to <literal>off</> where limited row visibility could cause
incorrect results; for example, <application>pg_dump</> makes that
change by default. This variable has no effect on roles which bypass
every row security policy, to wit, superusers and roles with
the <literal>BYPASSRLS</> attribute.
</para>
<para>
......
......@@ -1543,8 +1543,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
Row security policies can be specific to commands, or to roles, or to
both. The commands available are <literal>ALL</literal>,
<literal>SELECT</>, <literal>INSERT</>, <literal>UPDATE</>, and
<literal>DELETE</>. Multiple roles can be assigned to a given policy
and normal role membership and inheritance rules apply.
<literal>DELETE</>. Multiple roles can be assigned to a given policy and
normal role membership and inheritance rules apply. Table owners,
superusers, and roles with the <literal>BYPASSRLS</> attribute bypass the
row security system when querying a table. Applications that expect to
bypass all row security through those mechanisms should
set <xref linkend="guc-row-security"> to <literal>off</>.
</para>
<para>
......@@ -1574,17 +1578,6 @@ REVOKE ALL ON accounts FROM PUBLIC;
<xref linkend="sql-altertable"> command.
</para>
<para>
The table owners and superusers bypass the row security system when
querying a table. Any user can request that row security be bypassed by
setting <xref linkend="guc-row-security"> to <literal>off</literal>. If
the user does not have privileges to bypass row security when querying a
given table then an error will be returned instead. Other users can be
granted the ability to bypass the row security system with
the <literal>BYPASSRLS</literal> role attribute. This attribute can only
be set by a superuser.
</para>
<para>
Each policy has a name and multiple policies can be defined for a
table. As policies are table-specific, each policy for a table must
......
......@@ -196,16 +196,13 @@ CREATE ROLE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac
<term><literal>NOBYPASSRLS</literal></term>
<listitem>
<para>
These clauses determine whether a role is allowed to bypass row-level security (RLS)
policies. A role having the <literal>BYPASSRLS</literal> attribute will
be allowed to bypass row-security policies by setting
<literal>row_security</literal> to
<literal>OFF</literal>. <literal>NOBYPASSRLS</literal> is the default.
These clauses determine whether a role bypasses every row-level
security (RLS) policy. <literal>NOBYPASSRLS</literal> is the default.
Note that pg_dump will set <literal>row_security</literal> to
<literal>OFF</literal> by default, to ensure all contents of a table are
dumped out. If the user running pg_dump does not have appropriate
permissions, an error will be returned. The superuser and owner of the
table being dumped are considered to always have the right to bypass RLS.
table being dumped always bypass RLS.
</para>
</listitem>
</varlistentry>
......
......@@ -40,10 +40,8 @@ extern int check_enable_rls(Oid relid, Oid checkAsUser, bool noError);
* for the table and the plan cache needs to be invalidated if the environment
* changes.
*
* Handle checking as another role via checkAsUser (for views, etc). Note that
* if *not* checking as another role, the caller should pass InvalidOid rather
* than GetUserId(). Otherwise the check for row_security = OFF is skipped, and
* so we may falsely report that RLS is active when the user has bypassed it.
* Handle checking as another role via checkAsUser (for views, etc). Pass
* InvalidOid to check the current user.
*
* If noError is set to 'true' then we just return RLS_ENABLED instead of doing
* an ereport() if the user has attempted to bypass RLS and they are not
......@@ -78,32 +76,19 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
return RLS_NONE;
/*
* Check permissions
*
* Table owners always bypass RLS. Note that superuser is always
* considered an owner. Return RLS_NONE_ENV to indicate that this
* decision depends on the environment (in this case, the user_id).
* Table owners and BYPASSRLS users bypass RLS. Note that a superuser
* qualifies as both. Return RLS_NONE_ENV to indicate that this decision
* depends on the environment (in this case, the user_id).
*/
if (pg_class_ownercheck(relid, user_id))
if (pg_class_ownercheck(relid, user_id) ||
has_bypassrls_privilege(user_id))
return RLS_NONE_ENV;
/*
* If the row_security GUC is 'off', check if the user has permission to
* bypass RLS. row_security is always considered 'on' when querying
* through a view or other cases where checkAsUser is valid.
*/
if (!row_security && !checkAsUser)
{
if (has_bypassrls_privilege(user_id))
/* OK to bypass */
return RLS_NONE_ENV;
else if (noError)
return RLS_ENABLED;
else
/* row_security GUC says to bypass RLS, but user lacks permission */
if (!row_security && !noError)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("insufficient privilege to bypass row security.")));
}
/* RLS should be fully enabled for this relation. */
return RLS_ENABLED;
......
......@@ -51,7 +51,7 @@ CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MAC
bool rolcreatedb; /* allowed to create databases? */
bool rolcanlogin; /* allowed to log in as session user? */
bool rolreplication; /* role used for streaming replication */
bool rolbypassrls; /* allowed to bypass row level security? */
bool rolbypassrls; /* bypasses row level security? */
int32 rolconnlimit; /* max connections allowed (-1=no limit) */
/* remaining fields may be null; use heap_getattr to read them! */
......
......@@ -2584,10 +2584,15 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
0,cfcd208495d565ef66e7dff9f98764da
1,c4ca4238a0b923820dcc509a6f75849b
2,c81e728d9d4c2f636f067f89cc14862c
3,eccbc87e4b5ce2fe28308fd9f2a7baf3
4,a87ff679a2f3e71d9181a67b7542122c
5,e4da3b7fbbce2345d7772b0674a318d5
6,1679091c5a880faf6fb5e6087eb1b2dc
7,8f14e45fceea167a5a36dedd4bea2543
8,c9f0f895fb98ab9159f51fd0297e236d
9,45c48cce2e2d7fbdea1afc51c7c6ad26
10,d3d9446802a44259755d38e6d163e820
-- Check COPY TO as user without permissions. SET row_security TO OFF;
SET SESSION AUTHORIZATION rls_regress_user2;
......@@ -2627,6 +2632,7 @@ COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
1,c4ca4238a0b923820dcc509a6f75849b
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
1,c4ca4238a0b923820dcc509a6f75849b
-- Check COPY TO as user without permissions. SET row_security TO OFF;
SET SESSION AUTHORIZATION rls_regress_user2;
SET row_security TO OFF;
......@@ -2650,14 +2656,10 @@ SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
ERROR: COPY FROM not supported with row level security.
HINT: Use direct INSERT statements instead.
-- Check COPY TO as user with permissions and BYPASSRLS
-- Check COPY FROM as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
SET row_security TO OFF;
COPY copy_t FROM STDIN; --ok
SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
ERROR: COPY FROM not supported with row level security.
HINT: Use direct INSERT statements instead.
COPY copy_t FROM STDIN; --ok
-- Check COPY FROM as user without permissions.
SET SESSION AUTHORIZATION rls_regress_user2;
SET row_security TO OFF;
......
......@@ -1070,17 +1070,15 @@ COPY copy_t FROM STDIN; --fail - insufficient privilege to bypass rls.
SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-- Check COPY TO as user with permissions and BYPASSRLS
-- Check COPY FROM as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
SET row_security TO OFF;
SET row_security TO ON;
COPY copy_t FROM STDIN; --ok
1 abc
2 bcd
3 cde
4 def
\.
SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-- Check COPY FROM as user without permissions.
SET SESSION AUTHORIZATION rls_regress_user2;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment