aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ri_triggers.c
diff options
context:
space:
mode:
authorStephen Frost <sfrost@snowman.net>2015-01-12 17:04:11 -0500
committerStephen Frost <sfrost@snowman.net>2015-01-28 12:31:30 -0500
commit804b6b6db4dcfc590a468e7be390738f9f7755fb (patch)
tree334d58ccb1a76d04c9bb61309879d6c8e6a8f7e4 /src/backend/utils/adt/ri_triggers.c
parentacc2b1e843ae04e97025880f33a82f326ab12d59 (diff)
downloadpostgresql-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/utils/adt/ri_triggers.c')
-rw-r--r--src/backend/utils/adt/ri_triggers.c87
1 files changed, 68 insertions, 19 deletions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index e6dd4415077..f6bec8be9bc 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -43,6 +43,7 @@
#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
#include "miscadmin.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
@@ -50,6 +51,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/rls.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
@@ -3197,6 +3199,9 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
bool onfk;
const int16 *attnums;
int idx;
+ Oid rel_oid;
+ AclResult aclresult;
+ bool has_perm = true;
if (spi_err)
ereport(ERROR,
@@ -3215,37 +3220,75 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
if (onfk)
{
attnums = riinfo->fk_attnums;
+ rel_oid = fk_rel->rd_id;
if (tupdesc == NULL)
tupdesc = fk_rel->rd_att;
}
else
{
attnums = riinfo->pk_attnums;
+ rel_oid = pk_rel->rd_id;
if (tupdesc == NULL)
tupdesc = pk_rel->rd_att;
}
- /* Get printable versions of the keys involved */
- initStringInfo(&key_names);
- initStringInfo(&key_values);
- for (idx = 0; idx < riinfo->nkeys; idx++)
+ /*
+ * Check permissions- if the user does not have access to view the data in
+ * any of the key columns then we don't include the errdetail() below.
+ *
+ * Check if RLS is enabled on the relation first. If so, we don't return
+ * any specifics to avoid leaking data.
+ *
+ * Check table-level permissions next and, failing that, column-level
+ * privileges.
+ */
+
+ if (check_enable_rls(rel_oid, GetUserId(), true) != RLS_ENABLED)
{
- int fnum = attnums[idx];
- char *name,
- *val;
+ aclresult = pg_class_aclcheck(rel_oid, GetUserId(), ACL_SELECT);
+ if (aclresult != ACLCHECK_OK)
+ {
+ /* Try for column-level permissions */
+ for (idx = 0; idx < riinfo->nkeys; idx++)
+ {
+ aclresult = pg_attribute_aclcheck(rel_oid, attnums[idx],
+ GetUserId(),
+ ACL_SELECT);
- name = SPI_fname(tupdesc, fnum);
- val = SPI_getvalue(violator, tupdesc, fnum);
- if (!val)
- val = "null";
+ /* No access to the key */
+ if (aclresult != ACLCHECK_OK)
+ {
+ has_perm = false;
+ break;
+ }
+ }
+ }
+ }
- if (idx > 0)
+ if (has_perm)
+ {
+ /* Get printable versions of the keys involved */
+ initStringInfo(&key_names);
+ initStringInfo(&key_values);
+ for (idx = 0; idx < riinfo->nkeys; idx++)
{
- appendStringInfoString(&key_names, ", ");
- appendStringInfoString(&key_values, ", ");
+ int fnum = attnums[idx];
+ char *name,
+ *val;
+
+ name = SPI_fname(tupdesc, fnum);
+ val = SPI_getvalue(violator, tupdesc, fnum);
+ if (!val)
+ val = "null";
+
+ if (idx > 0)
+ {
+ appendStringInfoString(&key_names, ", ");
+ appendStringInfoString(&key_values, ", ");
+ }
+ appendStringInfoString(&key_names, name);
+ appendStringInfoString(&key_values, val);
}
- appendStringInfoString(&key_names, name);
- appendStringInfoString(&key_values, val);
}
if (onfk)
@@ -3254,9 +3297,12 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
RelationGetRelationName(fk_rel),
NameStr(riinfo->conname)),
- errdetail("Key (%s)=(%s) is not present in table \"%s\".",
- key_names.data, key_values.data,
- RelationGetRelationName(pk_rel)),
+ has_perm ?
+ errdetail("Key (%s)=(%s) is not present in table \"%s\".",
+ key_names.data, key_values.data,
+ RelationGetRelationName(pk_rel)) :
+ errdetail("Key is not present in table \"%s\".",
+ RelationGetRelationName(pk_rel)),
errtableconstraint(fk_rel, NameStr(riinfo->conname))));
else
ereport(ERROR,
@@ -3265,8 +3311,11 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
RelationGetRelationName(pk_rel),
NameStr(riinfo->conname),
RelationGetRelationName(fk_rel)),
+ has_perm ?
errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
key_names.data, key_values.data,
+ RelationGetRelationName(fk_rel)) :
+ errdetail("Key is still referenced from table \"%s\".",
RelationGetRelationName(fk_rel)),
errtableconstraint(fk_rel, NameStr(riinfo->conname))));
}