mirror of https://github.com/postgres/postgres
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.pull/14/head
parent
acc2b1e843
commit
804b6b6db4
@ -0,0 +1,114 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* rls.c |
||||
* RLS-related utility functions. |
||||
* |
||||
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* src/backend/utils/misc/rls.c |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "access/htup.h" |
||||
#include "access/htup_details.h" |
||||
#include "catalog/pg_class.h" |
||||
#include "miscadmin.h" |
||||
#include "utils/acl.h" |
||||
#include "utils/elog.h" |
||||
#include "utils/rls.h" |
||||
#include "utils/syscache.h" |
||||
|
||||
|
||||
extern int check_enable_rls(Oid relid, Oid checkAsUser, bool noError); |
||||
|
||||
/*
|
||||
* 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). |
||||
* |
||||
* 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 |
||||
* allowed to. This allows users to check if RLS is enabled without having to |
||||
* deal with the actual error case (eg: error cases which are trying to decide |
||||
* if the user should get data from the relation back as part of the error). |
||||
*/ |
||||
int |
||||
check_enable_rls(Oid relid, Oid checkAsUser, bool noError) |
||||
{ |
||||
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 |
||||
if (noError) |
||||
return RLS_ENABLED; |
||||
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; |
||||
} |
@ -0,0 +1,58 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* rls.h |
||||
* Header file for Row Level Security (RLS) utility commands to be used |
||||
* with the rowsecurity feature. |
||||
* |
||||
* Copyright (c) 2007-2015, PostgreSQL Global Development Group |
||||
* |
||||
* src/include/utils/rls.h |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#ifndef RLS_H |
||||
#define RLS_H |
||||
|
||||
/* GUC variable */ |
||||
extern int row_security; |
||||
|
||||
/* Possible values for row_security GUC */ |
||||
typedef enum RowSecurityConfigType |
||||
{ |
||||
ROW_SECURITY_OFF, /* RLS never applied- error thrown if no priv */ |
||||
ROW_SECURITY_ON, /* normal case, RLS applied for regular users */ |
||||
ROW_SECURITY_FORCE /* RLS applied for superusers and table owners */ |
||||
} RowSecurityConfigType; |
||||
|
||||
/*
|
||||
* Used by callers of check_enable_rls. |
||||
* |
||||
* RLS could be completely disabled on the tables involved in the query, |
||||
* which is the simple case, or it may depend on the current environment |
||||
* (the role which is running the query or the value of the row_security |
||||
* GUC- on, off, or force), or it might be simply enabled as usual. |
||||
* |
||||
* If RLS isn't on the table involved then RLS_NONE is returned to indicate |
||||
* that we don't need to worry about invalidating the query plan for RLS |
||||
* reasons. If RLS is on the table, but we are bypassing it for now, then |
||||
* we return RLS_NONE_ENV to indicate that, if the environment changes, |
||||
* we need to invalidate and replan. Finally, if RLS should be turned on |
||||
* for the query, then we return RLS_ENABLED, which means we also need to |
||||
* invalidate if the environment changes. |
||||
* |
||||
* Note that RLS_ENABLED will also be returned if noError is true |
||||
* (indicating that the caller simply want to know if RLS should be applied |
||||
* for this user but doesn't want an error thrown if it is; this is used |
||||
* by other error cases where we're just trying to decide if data from the |
||||
* table should be passed back to the user or not). |
||||
*/ |
||||
enum CheckEnableRlsResult |
||||
{ |
||||
RLS_NONE, |
||||
RLS_NONE_ENV, |
||||
RLS_ENABLED |
||||
}; |
||||
|
||||
extern int check_enable_rls(Oid relid, Oid checkAsUser, bool noError); |
||||
|
||||
#endif /* RLS_H */ |
Loading…
Reference in new issue