Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 5d35438

Browse files
committed
Adjust behavior of row_security GUC to match the docs.
Some time back we agreed that row_security=off should not be a way to bypass RLS entirely, but only a way to get an error if it was being applied. However, the code failed to act that way for table owners. Per discussion, this is a must-fix bug for 9.5.0. Adjust the logic in rls.c to behave as expected; also, modify the error message to be more consistent with the new interpretation. The regression tests need minor corrections as well. Also update the comments about row_security in ddl.sgml to be correct. (The official description of the GUC in config.sgml is already correct.) I failed to resist the temptation to do some other very minor cleanup as well, such as getting rid of a duplicate extern declaration.
1 parent 8978eb0 commit 5d35438

File tree

4 files changed

+78
-92
lines changed

4 files changed

+78
-92
lines changed

doc/src/sgml/ddl.sgml

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,11 +1572,7 @@ REVOKE ALL ON accounts FROM PUBLIC;
15721572
bypass the row security system when accessing a table. Table owners
15731573
normally bypass row security as well, though a table owner can choose to
15741574
be subject to row security with <link linkend="sql-altertable">ALTER
1575-
TABLE ... FORCE ROW LEVEL SECURITY</>. Even in a table with that option
1576-
selected, the table owner will bypass row security if the
1577-
<xref linkend="guc-row-security"> configuration parameter is set
1578-
to <literal>off</>; this setting is typically used for purposes such as
1579-
backup and restore.
1575+
TABLE ... FORCE ROW LEVEL SECURITY</>.
15801576
</para>
15811577

15821578
<para>
@@ -1606,14 +1602,6 @@ REVOKE ALL ON accounts FROM PUBLIC;
16061602
of all roles that they are a member of.
16071603
</para>
16081604

1609-
<para>
1610-
Referential integrity checks, such as unique or primary key constraints
1611-
and foreign key references, always bypass row security to ensure that
1612-
data integrity is maintained. Care must be taken when developing
1613-
schemas and row level policies to avoid <quote>covert channel</> leaks of
1614-
information through such referential integrity checks.
1615-
</para>
1616-
16171605
<para>
16181606
As a simple example, here is how to create a policy on
16191607
the <literal>account</> relation to allow only members of
@@ -1773,6 +1761,26 @@ postgres=&gt; update passwd set pwhash = 'abc';
17731761
UPDATE 1
17741762
</programlisting>
17751763

1764+
<para>
1765+
Referential integrity checks, such as unique or primary key constraints
1766+
and foreign key references, always bypass row security to ensure that
1767+
data integrity is maintained. Care must be taken when developing
1768+
schemas and row level policies to avoid <quote>covert channel</> leaks of
1769+
information through such referential integrity checks.
1770+
</para>
1771+
1772+
<para>
1773+
In some contexts it is important to be sure that row security is
1774+
not being applied. For example, when taking a backup, it could be
1775+
disastrous if row security silently caused some rows to be omitted
1776+
from the backup. In such a situation, you can set the
1777+
<xref linkend="guc-row-security"> configuration parameter
1778+
to <literal>off</>. This does not in itself bypass row security;
1779+
what it does is throw an error if any query's results would get filtered
1780+
by a policy. The reason for the error can then be investigated and
1781+
fixed.
1782+
</para>
1783+
17761784
<para>
17771785
For additional details see <xref linkend="sql-createpolicy">
17781786
and <xref linkend="sql-altertable">.

src/backend/utils/misc/rls.c

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,17 @@
1717
#include "access/htup.h"
1818
#include "access/htup_details.h"
1919
#include "access/transam.h"
20-
#include "catalog/pg_class.h"
2120
#include "catalog/namespace.h"
21+
#include "catalog/pg_class.h"
2222
#include "miscadmin.h"
2323
#include "utils/acl.h"
2424
#include "utils/builtins.h"
2525
#include "utils/elog.h"
26+
#include "utils/lsyscache.h"
2627
#include "utils/rls.h"
2728
#include "utils/syscache.h"
2829

2930

30-
extern int check_enable_rls(Oid relid, Oid checkAsUser, bool noError);
31-
3231
/*
3332
* check_enable_rls
3433
*
@@ -52,20 +51,21 @@ extern int check_enable_rls(Oid relid, Oid checkAsUser, bool noError);
5251
int
5352
check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
5453
{
54+
Oid user_id = checkAsUser ? checkAsUser : GetUserId();
5555
HeapTuple tuple;
5656
Form_pg_class classform;
5757
bool relrowsecurity;
5858
bool relforcerowsecurity;
59-
Oid user_id = checkAsUser ? checkAsUser : GetUserId();
59+
bool amowner;
6060

6161
/* Nothing to do for built-in relations */
62-
if (relid < FirstNormalObjectId)
62+
if (relid < (Oid) FirstNormalObjectId)
6363
return RLS_NONE;
6464

65+
/* Fetch relation's relrowsecurity and relforcerowsecurity flags */
6566
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
6667
if (!HeapTupleIsValid(tuple))
6768
return RLS_NONE;
68-
6969
classform = (Form_pg_class) GETSTRUCT(tuple);
7070

7171
relrowsecurity = classform->relrowsecurity;
@@ -88,41 +88,45 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
8888
return RLS_NONE_ENV;
8989

9090
/*
91-
* Table owners generally bypass RLS, except if row_security=true and the
92-
* table has been set (by an owner) to FORCE ROW SECURITY, and this is not
93-
* a referential integrity check.
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.
9494
*
9595
* Return RLS_NONE_ENV to indicate that this decision depends on the
9696
* environment (in this case, the user_id).
9797
*/
98-
if (pg_class_ownercheck(relid, user_id))
98+
amowner = pg_class_ownercheck(relid, user_id);
99+
if (amowner)
99100
{
100101
/*
101-
* If row_security=true and FORCE ROW LEVEL SECURITY has been set on
102-
* the relation then we return RLS_ENABLED to indicate that RLS should
103-
* still be applied. If we are in a SECURITY_NOFORCE_RLS context or if
104-
* row_security=false then we return RLS_NONE_ENV.
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.
105106
*
106-
* The SECURITY_NOFORCE_RLS indicates that we should not apply RLS even
107-
* if the table has FORCE RLS set- IF the current user is the owner.
108-
* This is specifically to ensure that referential integrity checks are
109-
* able to still run correctly.
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.
110111
*
111112
* This is intentionally only done after we have checked that the user
112113
* is the table owner, which should always be the case for referential
113114
* integrity checks.
114115
*/
115-
if (row_security && relforcerowsecurity && !InNoForceRLSOperation())
116-
return RLS_ENABLED;
117-
else
116+
if (!relforcerowsecurity || InNoForceRLSOperation())
118117
return RLS_NONE_ENV;
119118
}
120119

121-
/* row_security GUC says to bypass RLS, but user lacks permission */
120+
/*
121+
* We should apply RLS. However, the user may turn off the row_security
122+
* GUC to get a forced error instead.
123+
*/
122124
if (!row_security && !noError)
123125
ereport(ERROR,
124126
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
125-
errmsg("insufficient privilege to bypass row-level security")));
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));
126130

127131
/* RLS should be fully enabled for this relation. */
128132
return RLS_ENABLED;

src/test/regress/expected/rowsecurity.out

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2728,8 +2728,8 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
27282728
-- Check COPY TO as user with permissions.
27292729
SET SESSION AUTHORIZATION rls_regress_user1;
27302730
SET row_security TO OFF;
2731-
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
2732-
ERROR: insufficient privilege to bypass row-level security
2731+
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
2732+
ERROR: query would be affected by row-level security policy for table "copy_t"
27332733
SET row_security TO ON;
27342734
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
27352735
0,cfcd208495d565ef66e7dff9f98764da
@@ -2769,8 +2769,8 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
27692769
-- Check COPY TO as user without permissions. SET row_security TO OFF;
27702770
SET SESSION AUTHORIZATION rls_regress_user2;
27712771
SET row_security TO OFF;
2772-
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
2773-
ERROR: insufficient privilege to bypass row-level security
2772+
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
2773+
ERROR: query would be affected by row-level security policy for table "copy_t"
27742774
SET row_security TO ON;
27752775
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
27762776
ERROR: permission denied for relation copy_t
@@ -2793,8 +2793,8 @@ COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
27932793
-- Check COPY TO as user with permissions.
27942794
SET SESSION AUTHORIZATION rls_regress_user1;
27952795
SET row_security TO OFF;
2796-
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
2797-
ERROR: insufficient privilege to bypass row-level security
2796+
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
2797+
ERROR: query would be affected by row-level security policy for table "copy_rel_to"
27982798
SET row_security TO ON;
27992799
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
28002800
-- Check COPY TO as user with permissions and BYPASSRLS
@@ -2822,8 +2822,8 @@ COPY copy_t FROM STDIN; --ok
28222822
-- Check COPY FROM as user with permissions.
28232823
SET SESSION AUTHORIZATION rls_regress_user1;
28242824
SET row_security TO OFF;
2825-
COPY copy_t FROM STDIN; --fail - insufficient privilege to bypass rls.
2826-
ERROR: insufficient privilege to bypass row-level security
2825+
COPY copy_t FROM STDIN; --fail - would be affected by RLS.
2826+
ERROR: query would be affected by row-level security policy for table "copy_t"
28272827
SET row_security TO ON;
28282828
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
28292829
ERROR: COPY FROM not supported with row-level security
@@ -3181,8 +3181,7 @@ SET SESSION AUTHORIZATION rls_regress_user0;
31813181
DROP TABLE r1;
31823182
DROP TABLE r2;
31833183
--
3184-
-- FORCE ROW LEVEL SECURITY applies RLS to owners but
3185-
-- only when row_security = on
3184+
-- FORCE ROW LEVEL SECURITY applies RLS to owners too
31863185
--
31873186
SET SESSION AUTHORIZATION rls_regress_user0;
31883187
SET row_security = on;
@@ -3215,30 +3214,16 @@ TABLE r1;
32153214
(0 rows)
32163215

32173216
SET row_security = off;
3218-
-- Shows all rows
3217+
-- these all fail, would be affected by RLS
32193218
TABLE r1;
3220-
a
3221-
----
3222-
10
3223-
20
3224-
(2 rows)
3225-
3226-
-- Update all rows
3219+
ERROR: query would be affected by row-level security policy for table "r1"
3220+
HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
32273221
UPDATE r1 SET a = 1;
3228-
TABLE r1;
3229-
a
3230-
---
3231-
1
3232-
1
3233-
(2 rows)
3234-
3235-
-- Delete all rows
3222+
ERROR: query would be affected by row-level security policy for table "r1"
3223+
HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
32363224
DELETE FROM r1;
3237-
TABLE r1;
3238-
a
3239-
---
3240-
(0 rows)
3241-
3225+
ERROR: query would be affected by row-level security policy for table "r1"
3226+
HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
32423227
DROP TABLE r1;
32433228
--
32443229
-- FORCE ROW LEVEL SECURITY does not break RI
@@ -3349,14 +3334,10 @@ TABLE r1;
33493334
(0 rows)
33503335

33513336
SET row_security = off;
3352-
-- Rows shown now
3337+
-- fail, would be affected by RLS
33533338
TABLE r1;
3354-
a
3355-
----
3356-
10
3357-
20
3358-
(2 rows)
3359-
3339+
ERROR: query would be affected by row-level security policy for table "r1"
3340+
HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
33603341
SET row_security = on;
33613342
-- Error
33623343
INSERT INTO r1 VALUES (10), (20) RETURNING *;
@@ -3377,7 +3358,7 @@ ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
33773358
-- Works fine
33783359
UPDATE r1 SET a = 30;
33793360
-- Show updated rows
3380-
SET row_security = off;
3361+
ALTER TABLE r1 NO FORCE ROW LEVEL SECURITY;
33813362
TABLE r1;
33823363
a
33833364
----
@@ -3393,7 +3374,7 @@ TABLE r1;
33933374
10
33943375
(1 row)
33953376

3396-
SET row_security = on;
3377+
ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
33973378
-- Error
33983379
UPDATE r1 SET a = 30 RETURNING *;
33993380
ERROR: new row violates row-level security policy for table "r1"

src/test/regress/sql/rowsecurity.sql

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,7 +1014,7 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
10141014
-- Check COPY TO as user with permissions.
10151015
SET SESSION AUTHORIZATION rls_regress_user1;
10161016
SET row_security TO OFF;
1017-
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
1017+
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
10181018
SET row_security TO ON;
10191019
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
10201020

@@ -1028,7 +1028,7 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
10281028
-- Check COPY TO as user without permissions. SET row_security TO OFF;
10291029
SET SESSION AUTHORIZATION rls_regress_user2;
10301030
SET row_security TO OFF;
1031-
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
1031+
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
10321032
SET row_security TO ON;
10331033
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
10341034

@@ -1054,7 +1054,7 @@ COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
10541054
-- Check COPY TO as user with permissions.
10551055
SET SESSION AUTHORIZATION rls_regress_user1;
10561056
SET row_security TO OFF;
1057-
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
1057+
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
10581058
SET row_security TO ON;
10591059
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
10601060

@@ -1092,7 +1092,7 @@ COPY copy_t FROM STDIN; --ok
10921092
-- Check COPY FROM as user with permissions.
10931093
SET SESSION AUTHORIZATION rls_regress_user1;
10941094
SET row_security TO OFF;
1095-
COPY copy_t FROM STDIN; --fail - insufficient privilege to bypass rls.
1095+
COPY copy_t FROM STDIN; --fail - would be affected by RLS.
10961096
SET row_security TO ON;
10971097
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
10981098

@@ -1315,8 +1315,7 @@ DROP TABLE r1;
13151315
DROP TABLE r2;
13161316

13171317
--
1318-
-- FORCE ROW LEVEL SECURITY applies RLS to owners but
1319-
-- only when row_security = on
1318+
-- FORCE ROW LEVEL SECURITY applies RLS to owners too
13201319
--
13211320
SET SESSION AUTHORIZATION rls_regress_user0;
13221321
SET row_security = on;
@@ -1342,16 +1341,10 @@ DELETE FROM r1;
13421341
TABLE r1;
13431342

13441343
SET row_security = off;
1345-
-- Shows all rows
1344+
-- these all fail, would be affected by RLS
13461345
TABLE r1;
1347-
1348-
-- Update all rows
13491346
UPDATE r1 SET a = 1;
1350-
TABLE r1;
1351-
1352-
-- Delete all rows
13531347
DELETE FROM r1;
1354-
TABLE r1;
13551348

13561349
DROP TABLE r1;
13571350

@@ -1469,7 +1462,7 @@ INSERT INTO r1 VALUES (10), (20);
14691462
TABLE r1;
14701463

14711464
SET row_security = off;
1472-
-- Rows shown now
1465+
-- fail, would be affected by RLS
14731466
TABLE r1;
14741467

14751468
SET row_security = on;
@@ -1497,15 +1490,15 @@ ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
14971490
UPDATE r1 SET a = 30;
14981491

14991492
-- Show updated rows
1500-
SET row_security = off;
1493+
ALTER TABLE r1 NO FORCE ROW LEVEL SECURITY;
15011494
TABLE r1;
15021495
-- reset value in r1 for test with RETURNING
15031496
UPDATE r1 SET a = 10;
15041497

15051498
-- Verify row reset
15061499
TABLE r1;
15071500

1508-
SET row_security = on;
1501+
ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
15091502

15101503
-- Error
15111504
UPDATE r1 SET a = 30 RETURNING *;

0 commit comments

Comments
 (0)