Make inherited TRUNCATE perform access permission checks on parent table only.

Previously, TRUNCATE command through a parent table checked the
permissions on not only the parent table but also the children tables
inherited from it. This was a bug and inherited queries should perform
access permission checks on the parent table only. This commit fixes
that bug.

Back-patch to all supported branches.

Author: Amit Langote
Reviewed-by: Fujii Masao
Discussion: https://postgr.es/m/CAHGQGwFHdSvifhJE+-GSNqUHSfbiKxaeQQ7HGcYz6SC2n_oDcg@mail.gmail.com
This commit is contained in:
Fujii Masao 2020-01-31 00:46:20 +09:00
parent 8fc33e6cc1
commit 56bc82a511
3 changed files with 84 additions and 18 deletions

View File

@ -262,7 +262,9 @@ struct DropRelationCallbackState
#define ATT_COMPOSITE_TYPE 0x0010
#define ATT_FOREIGN_TABLE 0x0020
static void truncate_check_rel(Relation rel);
static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
static void truncate_check_activity(Relation rel);
static List *MergeAttributes(List *schema, List *supers, char relpersistence,
List **supOids, List **supconstr, int *supOidCount);
static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
@ -1018,7 +1020,11 @@ ExecuteTruncate(TruncateStmt *stmt)
heap_close(rel, lockmode);
continue;
}
truncate_check_rel(rel);
truncate_check_rel(myrelid, rel->rd_rel);
truncate_check_perms(myrelid, rel->rd_rel);
truncate_check_activity(rel);
rels = lappend(rels, rel);
relids = lappend_oid(relids, myrelid);
@ -1054,7 +1060,15 @@ ExecuteTruncate(TruncateStmt *stmt)
continue;
}
truncate_check_rel(rel);
/*
* Inherited TRUNCATE commands perform access
* permission checks on the parent table only.
* So we skip checking the children's permissions
* and don't call truncate_check_perms() here.
*/
truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
truncate_check_activity(rel);
rels = lappend(rels, rel);
relids = lappend_oid(relids, childrelid);
}
@ -1088,7 +1102,9 @@ ExecuteTruncate(TruncateStmt *stmt)
ereport(NOTICE,
(errmsg("truncate cascades to table \"%s\"",
RelationGetRelationName(rel))));
truncate_check_rel(rel);
truncate_check_rel(relid, rel->rd_rel);
truncate_check_perms(relid, rel->rd_rel);
truncate_check_activity(rel);
rels = lappend(rels, rel);
relids = lappend_oid(relids, relid);
}
@ -1289,30 +1305,45 @@ ExecuteTruncate(TruncateStmt *stmt)
* Check that a given rel is safe to truncate. Subroutine for ExecuteTruncate
*/
static void
truncate_check_rel(Relation rel)
truncate_check_rel(Oid relid, Form_pg_class reltuple)
{
AclResult aclresult;
char *relname = NameStr(reltuple->relname);
/* Only allow truncate on regular tables */
if (rel->rd_rel->relkind != RELKIND_RELATION)
if (reltuple->relkind != RELKIND_RELATION)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table",
RelationGetRelationName(rel))));
errmsg("\"%s\" is not a table", relname)));
/* Permissions checks */
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
ACL_TRUNCATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
if (!allowSystemTableMods && IsSystemRelation(rel))
if (!allowSystemTableMods && IsSystemClass(relid, reltuple))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied: \"%s\" is a system catalog",
RelationGetRelationName(rel))));
relname)));
}
/*
* Check that current user has the permission to truncate given relation.
*/
static void
truncate_check_perms(Oid relid, Form_pg_class reltuple)
{
char *relname = NameStr(reltuple->relname);
AclResult aclresult;
/* Permissions checks */
aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_CLASS, relname);
}
/*
* Set of extra sanity checks to check if a given relation is safe to
* truncate.
*/
static void
truncate_check_activity(Relation rel)
{
/*
* Don't allow truncate on temp tables of other backends ... their local
* buffer manager is not going to cope.

View File

@ -656,6 +656,27 @@ SELECT oid FROM atestp2; -- ok
-----
(0 rows)
-- child's permissions do not apply when operating on parent
SET SESSION AUTHORIZATION regressuser1;
REVOKE ALL ON atestc FROM regressuser2;
GRANT ALL ON atestp1 TO regressuser2;
SET SESSION AUTHORIZATION regressuser2;
SELECT f2 FROM atestp1; -- ok
f2
----
(0 rows)
SELECT f2 FROM atestc; -- fail
ERROR: permission denied for relation atestc
DELETE FROM atestp1; -- ok
DELETE FROM atestc; -- fail
ERROR: permission denied for relation atestc
UPDATE atestp1 SET f1 = 1; -- ok
UPDATE atestc SET f1 = 1; -- fail
ERROR: permission denied for relation atestc
TRUNCATE atestp1; -- ok
TRUNCATE atestc; -- fail
ERROR: permission denied for relation atestc
-- privileges on functions, languages
-- switch to superuser
\c -

View File

@ -414,6 +414,20 @@ SELECT fy FROM atestp2; -- ok
SELECT atestp2 FROM atestp2; -- ok
SELECT oid FROM atestp2; -- ok
-- child's permissions do not apply when operating on parent
SET SESSION AUTHORIZATION regressuser1;
REVOKE ALL ON atestc FROM regressuser2;
GRANT ALL ON atestp1 TO regressuser2;
SET SESSION AUTHORIZATION regressuser2;
SELECT f2 FROM atestp1; -- ok
SELECT f2 FROM atestc; -- fail
DELETE FROM atestp1; -- ok
DELETE FROM atestc; -- fail
UPDATE atestp1 SET f1 = 1; -- ok
UPDATE atestc SET f1 = 1; -- fail
TRUNCATE atestp1; -- ok
TRUNCATE atestc; -- fail
-- privileges on functions, languages
-- switch to superuser