diff options
author | Stephen Frost <sfrost@snowman.net> | 2015-01-12 17:04:11 -0500 |
---|---|---|
committer | Stephen Frost <sfrost@snowman.net> | 2015-01-28 12:31:30 -0500 |
commit | 804b6b6db4dcfc590a468e7be390738f9f7755fb (patch) | |
tree | 334d58ccb1a76d04c9bb61309879d6c8e6a8f7e4 /src/backend/rewrite/rowsecurity.c | |
parent | acc2b1e843ae04e97025880f33a82f326ab12d59 (diff) | |
download | postgresql-804b6b6db4dcfc590a468e7be390738f9f7755fb.tar.gz postgresql-804b6b6db4dcfc590a468e7be390738f9f7755fb.zip |
Fix column-privilege leak in error-message paths
While building error messages to return to the user,
BuildIndexValueDescription, ExecBuildSlotValueDescription and
ri_ReportViolation would happily include the entire key or entire row in
the result returned to the user, even if the user didn't have access to
view all of the columns being included.
Instead, include only those columns which the user is providing or which
the user has select rights on. If the user does not have any rights
to view the table or any of the columns involved then no detail is
provided and a NULL value is returned from BuildIndexValueDescription
and ExecBuildSlotValueDescription. Note that, for key cases, the user
must have access to all of the columns for the key to be shown; a
partial key will not be returned.
Further, in master only, do not return any data for cases where row
security is enabled on the relation and row security should be applied
for the user. This required a bit of refactoring and moving of things
around related to RLS- note the addition of utils/misc/rls.c.
Back-patch all the way, as column-level privileges are now in all
supported versions.
This has been assigned CVE-2014-8161, but since the issue and the patch
have already been publicized on pgsql-hackers, there's no point in trying
to hide this commit.
Diffstat (limited to 'src/backend/rewrite/rowsecurity.c')
-rw-r--r-- | src/backend/rewrite/rowsecurity.c | 81 |
1 files changed, 2 insertions, 79 deletions
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index 8f8e291fb88..7669130e2b6 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -52,6 +52,7 @@ #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/rel.h" +#include "utils/rls.h" #include "utils/syscache.h" #include "tcop/utility.h" @@ -115,7 +116,7 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index) return false; /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser); + rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -456,84 +457,6 @@ process_policies(List *policies, int rt_index, Expr **qual_eval, } /* - * check_enable_rls - * - * Determine, based on the relation, row_security setting, and current role, - * if RLS is applicable to this query. RLS_NONE_ENV indicates that, while - * RLS is not to be added for this query, a change in the environment may change - * that. RLS_NONE means that RLS is not on the relation at all and therefore - * we don't need to worry about it. RLS_ENABLED means RLS should be implemented - * 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). - */ -int -check_enable_rls(Oid relid, Oid checkAsUser) -{ - HeapTuple tuple; - Form_pg_class classform; - bool relrowsecurity; - Oid user_id = checkAsUser ? checkAsUser : GetUserId(); - - tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); - if (!HeapTupleIsValid(tuple)) - return RLS_NONE; - - classform = (Form_pg_class) GETSTRUCT(tuple); - - relrowsecurity = classform->relrowsecurity; - - ReleaseSysCache(tuple); - - /* Nothing to do if the relation does not have RLS */ - if (!relrowsecurity) - return RLS_NONE; - - /* - * Check permissions - * - * If the relation has row level security enabled and the row_security GUC - * is off, then check if the user has rights to bypass RLS for this - * relation. Table owners can always bypass, as can any role with the - * BYPASSRLS capability. - * - * If the role is the table owner, then we bypass RLS unless row_security - * is set to 'force'. Note that superuser is always considered an owner. - * - * Return RLS_NONE_ENV to indicate that this decision depends on the - * environment (in this case, what the current values of user_id and - * row_security are). - */ - if (row_security != ROW_SECURITY_FORCE - && (pg_class_ownercheck(relid, user_id))) - return RLS_NONE_ENV; - - /* - * If the row_security GUC is 'off' then check if the user has permission - * to bypass it. Note that we have already handled the case where the user - * is the table owner above. - * - * Note that row_security is always considered 'on' when querying - * through a view or other cases where checkAsUser is true, so skip this - * if checkAsUser is in use. - */ - if (!checkAsUser && row_security == ROW_SECURITY_OFF) - { - if (has_bypassrls_privilege(user_id)) - /* OK to bypass */ - return RLS_NONE_ENV; - else - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("insufficient privilege to bypass row security."))); - } - - /* RLS should be fully enabled for this relation. */ - return RLS_ENABLED; -} - -/* * check_role_for_policy - * determines if the policy should be applied for the current role */ |