aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/misc/rls.c
diff options
context:
space:
mode:
authorStephen Frost <sfrost@snowman.net>2015-10-04 21:05:18 -0400
committerStephen Frost <sfrost@snowman.net>2015-10-04 21:05:18 -0400
commit90f334d2ca1a8bae2d0cd8a0898fb8ef90257565 (patch)
tree2619e5544fbeb3ae65c462a3a9dd3f1a97d8ca11 /src/backend/utils/misc/rls.c
parente78dc6b829219cacaccc59957b5375585e919099 (diff)
downloadpostgresql-90f334d2ca1a8bae2d0cd8a0898fb8ef90257565.tar.gz
postgresql-90f334d2ca1a8bae2d0cd8a0898fb8ef90257565.zip
ALTER TABLE .. FORCE ROW LEVEL SECURITY
To allow users to force RLS to always be applied, even for table owners, add ALTER TABLE .. FORCE ROW LEVEL SECURITY. row_security=off overrides FORCE ROW LEVEL SECURITY, to ensure pg_dump output is complete (by default). Also add SECURITY_NOFORCE_RLS context to avoid data corruption when ALTER TABLE .. FORCE ROW SECURITY is being used. The SECURITY_NOFORCE_RLS security context is used only during referential integrity checks and is only considered in check_enable_rls() after we have already checked that the current user is the owner of the relation (which should always be the case during referential integrity checks). Back-patch to 9.5 where RLS was added.
Diffstat (limited to 'src/backend/utils/misc/rls.c')
-rw-r--r--src/backend/utils/misc/rls.c44
1 files changed, 39 insertions, 5 deletions
diff --git a/src/backend/utils/misc/rls.c b/src/backend/utils/misc/rls.c
index eaf9d6e66ff..6ce92af0199 100644
--- a/src/backend/utils/misc/rls.c
+++ b/src/backend/utils/misc/rls.c
@@ -55,6 +55,7 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
HeapTuple tuple;
Form_pg_class classform;
bool relrowsecurity;
+ bool relforcerowsecurity;
Oid user_id = checkAsUser ? checkAsUser : GetUserId();
/* Nothing to do for built-in relations */
@@ -68,6 +69,7 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
classform = (Form_pg_class) GETSTRUCT(tuple);
relrowsecurity = classform->relrowsecurity;
+ relforcerowsecurity = classform->relforcerowsecurity;
ReleaseSysCache(tuple);
@@ -76,14 +78,46 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
return RLS_NONE;
/*
- * 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).
+ * BYPASSRLS users always bypass RLS. Note that superusers are always
+ * considered to have BYPASSRLS.
+ *
+ * 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) ||
- has_bypassrls_privilege(user_id))
+ if (has_bypassrls_privilege(user_id))
return RLS_NONE_ENV;
+ /*
+ * Table owners generally bypass RLS, except if row_security=true and the
+ * table has been set (by an owner) to FORCE ROW SECURITY, and this is not
+ * a referential integrity check.
+ *
+ * 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 row_security=true and FORCE ROW LEVEL SECURITY has been set on
+ * the relation then we return RLS_ENABLED to indicate that RLS should
+ * still be applied. If we are in a SECURITY_NOFORCE_RLS context or if
+ * row_security=false then we return RLS_NONE_ENV.
+ *
+ * The SECURITY_NOFORCE_RLS indicates that we should not apply RLS even
+ * if the table has FORCE RLS set- IF the current user is the owner.
+ * This is specifically to ensure that referential integrity checks are
+ * able to still run correctly.
+ *
+ * This is intentionally only done after we have checked that the user
+ * is the table owner, which should always be the case for referential
+ * integrity checks.
+ */
+ if (row_security && relforcerowsecurity && !InNoForceRLSOperation())
+ return RLS_ENABLED;
+ else
+ return RLS_NONE_ENV;
+ }
+
/* row_security GUC says to bypass RLS, but user lacks permission */
if (!row_security && !noError)
ereport(ERROR,