1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-19 17:02:53 +03:00

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.

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.
This commit is contained in:
Stephen Frost
2015-01-12 17:04:11 -05:00
parent 350f1e7a8c
commit d49f84b084
10 changed files with 345 additions and 71 deletions

View File

@@ -42,6 +42,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"
@@ -3496,6 +3497,9 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
bool onfk;
int idx,
key_idx;
Oid rel_oid;
AclResult aclresult;
bool has_perm = true;
if (spi_err)
ereport(ERROR,
@@ -3514,12 +3518,14 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
if (onfk)
{
key_idx = RI_KEYPAIR_FK_IDX;
rel_oid = fk_rel->rd_id;
if (tupdesc == NULL)
tupdesc = fk_rel->rd_att;
}
else
{
key_idx = RI_KEYPAIR_PK_IDX;
rel_oid = pk_rel->rd_id;
if (tupdesc == NULL)
tupdesc = pk_rel->rd_att;
}
@@ -3539,45 +3545,81 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
RelationGetRelationName(pk_rel))));
}
/* Get printable versions of the keys involved */
initStringInfo(&key_names);
initStringInfo(&key_values);
for (idx = 0; idx < qkey->nkeypairs; 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 table-level permissions first and, failing that, column-level
* privileges.
*/
aclresult = pg_class_aclcheck(rel_oid, GetUserId(), ACL_SELECT);
if (aclresult != ACLCHECK_OK)
{
int fnum = qkey->keypair[idx][key_idx];
char *name,
*val;
name = SPI_fname(tupdesc, fnum);
val = SPI_getvalue(violator, tupdesc, fnum);
if (!val)
val = "null";
if (idx > 0)
/* Try for column-level permissions */
for (idx = 0; idx < qkey->nkeypairs; idx++)
{
appendStringInfoString(&key_names, ", ");
appendStringInfoString(&key_values, ", ");
aclresult = pg_attribute_aclcheck(rel_oid, qkey->keypair[idx][key_idx],
GetUserId(),
ACL_SELECT);
/* No access to the key */
if (aclresult != ACLCHECK_OK)
{
has_perm = false;
break;
}
}
}
if (has_perm)
{
/* Get printable versions of the keys involved */
initStringInfo(&key_names);
initStringInfo(&key_values);
for (idx = 0; idx < qkey->nkeypairs; idx++)
{
int fnum = qkey->keypair[idx][key_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)
ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
RelationGetRelationName(fk_rel), constrname),
errdetail("Key (%s)=(%s) is not present in table \"%s\".",
key_names.data, key_values.data,
RelationGetRelationName(pk_rel))));
RelationGetRelationName(fk_rel),
constrname),
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))));
else
ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
RelationGetRelationName(pk_rel),
constrname, RelationGetRelationName(fk_rel)),
constrname,
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))));
}

View File

@@ -3074,6 +3074,7 @@ comparetup_index_btree(const SortTuple *a, const SortTuple *b,
{
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
char *key_desc;
/*
* Some rather brain-dead implementations of qsort (such as the one in
@@ -3084,13 +3085,15 @@ comparetup_index_btree(const SortTuple *a, const SortTuple *b,
Assert(tuple1 != tuple2);
index_deform_tuple(tuple1, tupDes, values, isnull);
key_desc = BuildIndexValueDescription(state->indexRel, values, isnull);
ereport(ERROR,
(errcode(ERRCODE_UNIQUE_VIOLATION),
errmsg("could not create unique index \"%s\"",
RelationGetRelationName(state->indexRel)),
errdetail("Key %s is duplicated.",
BuildIndexValueDescription(state->indexRel,
values, isnull))));
key_desc ? errdetail("Key %s is duplicated.", key_desc) :
errdetail("Duplicate keys exist.")));
}
/*