diff options
author | Joe Conway <mail@joeconway.com> | 2015-07-28 13:21:37 -0700 |
---|---|---|
committer | Joe Conway <mail@joeconway.com> | 2015-07-28 13:21:37 -0700 |
commit | cfa928ff6f944ac101802718f64db942060187b1 (patch) | |
tree | ec49fee19907330ea2de0d5ef8486051992689b1 /src/backend/utils/misc/rls.c | |
parent | 6087d952b31fce56642e1c63cfed243aeb4d09bd (diff) | |
download | postgresql-cfa928ff6f944ac101802718f64db942060187b1.tar.gz postgresql-cfa928ff6f944ac101802718f64db942060187b1.zip |
Plug RLS related information leak in pg_stats view.
The pg_stats view is supposed to be restricted to only show rows
about tables the user can read. However, it sometimes can leak
information which could not otherwise be seen when row level security
is enabled. Fix that by not showing pg_stats rows to users that would
be subject to RLS on the table the row is related to. This is done
by creating/using the newly introduced SQL visible function,
row_security_active().
Along the way, clean up three call sites of check_enable_rls(). The second
argument of that function should only be specified as other than
InvalidOid when we are checking as a different user than the current one,
as in when querying through a view. These sites were passing GetUserId()
instead of InvalidOid, which can cause the function to return incorrect
results if the current user has the BYPASSRLS privilege and row_security
has been set to OFF.
Additionally fix a bug causing RI Trigger error messages to unintentionally
leak information when RLS is enabled, and other minor cleanup and
improvements. Also add WITH (security_barrier) to the definition of pg_stats.
Bumped CATVERSION due to new SQL functions and pg_stats view definition.
Back-patch to 9.5 where RLS was introduced. Reported by Yaroslav.
Patch by Joe Conway and Dean Rasheed with review and input by
Michael Paquier and Stephen Frost.
Diffstat (limited to 'src/backend/utils/misc/rls.c')
-rw-r--r-- | src/backend/utils/misc/rls.c | 53 |
1 files changed, 52 insertions, 1 deletions
diff --git a/src/backend/utils/misc/rls.c b/src/backend/utils/misc/rls.c index 44cb3743034..7b8d51d956f 100644 --- a/src/backend/utils/misc/rls.c +++ b/src/backend/utils/misc/rls.c @@ -16,9 +16,12 @@ #include "access/htup.h" #include "access/htup_details.h" +#include "access/transam.h" #include "catalog/pg_class.h" +#include "catalog/namespace.h" #include "miscadmin.h" #include "utils/acl.h" +#include "utils/builtins.h" #include "utils/elog.h" #include "utils/rls.h" #include "utils/syscache.h" @@ -37,7 +40,10 @@ 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). + * 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. * * 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 @@ -53,6 +59,17 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError) bool relrowsecurity; Oid user_id = checkAsUser ? checkAsUser : GetUserId(); + /* Nothing to do for built-in relations */ + if (relid < FirstNormalObjectId) + return RLS_NONE; + + /* + * Check if we have been told to explicitly skip RLS (perhaps because this + * is a foreign key check) + */ + if (InRowLevelSecurityDisabled()) + return RLS_NONE; + tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tuple)) return RLS_NONE; @@ -111,3 +128,37 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError) /* RLS should be fully enabled for this relation. */ return RLS_ENABLED; } + +/* + * row_security_active + * + * check_enable_rls wrapped as a SQL callable function except + * RLS_NONE_ENV and RLS_NONE are the same for this purpose. + */ +Datum +row_security_active(PG_FUNCTION_ARGS) +{ + /* By OID */ + Oid tableoid = PG_GETARG_OID(0); + int rls_status; + + rls_status = check_enable_rls(tableoid, InvalidOid, true); + PG_RETURN_BOOL(rls_status == RLS_ENABLED); +} + +Datum +row_security_active_name(PG_FUNCTION_ARGS) +{ + /* By qualified name */ + text *tablename = PG_GETARG_TEXT_P(0); + RangeVar *tablerel; + Oid tableoid; + int rls_status; + + /* Look up table name. Can't lock it - we might not have privileges. */ + tablerel = makeRangeVarFromNameList(textToQualifiedNameList(tablename)); + tableoid = RangeVarGetRelid(tablerel, NoLock, false); + + rls_status = check_enable_rls(tableoid, InvalidOid, true); + PG_RETURN_BOOL(rls_status == RLS_ENABLED); +} |