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