Skip to content

Commit 56bc82a

Browse files
committed
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
1 parent 8fc33e6 commit 56bc82a

File tree

3 files changed

+84
-18
lines changed

3 files changed

+84
-18
lines changed

src/backend/commands/tablecmds.c

+49-18
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,9 @@ struct DropRelationCallbackState
262262
#define ATT_COMPOSITE_TYPE 0x0010
263263
#define ATT_FOREIGN_TABLE 0x0020
264264

265-
static void truncate_check_rel(Relation rel);
265+
static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
266+
static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
267+
static void truncate_check_activity(Relation rel);
266268
static List *MergeAttributes(List *schema, List *supers, char relpersistence,
267269
List **supOids, List **supconstr, int *supOidCount);
268270
static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
@@ -1018,7 +1020,11 @@ ExecuteTruncate(TruncateStmt *stmt)
10181020
heap_close(rel, lockmode);
10191021
continue;
10201022
}
1021-
truncate_check_rel(rel);
1023+
1024+
truncate_check_rel(myrelid, rel->rd_rel);
1025+
truncate_check_perms(myrelid, rel->rd_rel);
1026+
truncate_check_activity(rel);
1027+
10221028
rels = lappend(rels, rel);
10231029
relids = lappend_oid(relids, myrelid);
10241030

@@ -1054,7 +1060,15 @@ ExecuteTruncate(TruncateStmt *stmt)
10541060
continue;
10551061
}
10561062

1057-
truncate_check_rel(rel);
1063+
/*
1064+
* Inherited TRUNCATE commands perform access
1065+
* permission checks on the parent table only.
1066+
* So we skip checking the children's permissions
1067+
* and don't call truncate_check_perms() here.
1068+
*/
1069+
truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1070+
truncate_check_activity(rel);
1071+
10581072
rels = lappend(rels, rel);
10591073
relids = lappend_oid(relids, childrelid);
10601074
}
@@ -1088,7 +1102,9 @@ ExecuteTruncate(TruncateStmt *stmt)
10881102
ereport(NOTICE,
10891103
(errmsg("truncate cascades to table \"%s\"",
10901104
RelationGetRelationName(rel))));
1091-
truncate_check_rel(rel);
1105+
truncate_check_rel(relid, rel->rd_rel);
1106+
truncate_check_perms(relid, rel->rd_rel);
1107+
truncate_check_activity(rel);
10921108
rels = lappend(rels, rel);
10931109
relids = lappend_oid(relids, relid);
10941110
}
@@ -1289,30 +1305,45 @@ ExecuteTruncate(TruncateStmt *stmt)
12891305
* Check that a given rel is safe to truncate. Subroutine for ExecuteTruncate
12901306
*/
12911307
static void
1292-
truncate_check_rel(Relation rel)
1308+
truncate_check_rel(Oid relid, Form_pg_class reltuple)
12931309
{
1294-
AclResult aclresult;
1310+
char *relname = NameStr(reltuple->relname);
12951311

12961312
/* Only allow truncate on regular tables */
1297-
if (rel->rd_rel->relkind != RELKIND_RELATION)
1313+
if (reltuple->relkind != RELKIND_RELATION)
12981314
ereport(ERROR,
12991315
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
1300-
errmsg("\"%s\" is not a table",
1301-
RelationGetRelationName(rel))));
1316+
errmsg("\"%s\" is not a table", relname)));
13021317

1303-
/* Permissions checks */
1304-
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
1305-
ACL_TRUNCATE);
1306-
if (aclresult != ACLCHECK_OK)
1307-
aclcheck_error(aclresult, ACL_KIND_CLASS,
1308-
RelationGetRelationName(rel));
1309-
1310-
if (!allowSystemTableMods && IsSystemRelation(rel))
1318+
if (!allowSystemTableMods && IsSystemClass(relid, reltuple))
13111319
ereport(ERROR,
13121320
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
13131321
errmsg("permission denied: \"%s\" is a system catalog",
1314-
RelationGetRelationName(rel))));
1322+
relname)));
1323+
}
13151324

1325+
/*
1326+
* Check that current user has the permission to truncate given relation.
1327+
*/
1328+
static void
1329+
truncate_check_perms(Oid relid, Form_pg_class reltuple)
1330+
{
1331+
char *relname = NameStr(reltuple->relname);
1332+
AclResult aclresult;
1333+
1334+
/* Permissions checks */
1335+
aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
1336+
if (aclresult != ACLCHECK_OK)
1337+
aclcheck_error(aclresult, ACL_KIND_CLASS, relname);
1338+
}
1339+
1340+
/*
1341+
* Set of extra sanity checks to check if a given relation is safe to
1342+
* truncate.
1343+
*/
1344+
static void
1345+
truncate_check_activity(Relation rel)
1346+
{
13161347
/*
13171348
* Don't allow truncate on temp tables of other backends ... their local
13181349
* buffer manager is not going to cope.

src/test/regress/expected/privileges.out

+21
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,27 @@ SELECT oid FROM atestp2; -- ok
656656
-----
657657
(0 rows)
658658

659+
-- child's permissions do not apply when operating on parent
660+
SET SESSION AUTHORIZATION regressuser1;
661+
REVOKE ALL ON atestc FROM regressuser2;
662+
GRANT ALL ON atestp1 TO regressuser2;
663+
SET SESSION AUTHORIZATION regressuser2;
664+
SELECT f2 FROM atestp1; -- ok
665+
f2
666+
----
667+
(0 rows)
668+
669+
SELECT f2 FROM atestc; -- fail
670+
ERROR: permission denied for relation atestc
671+
DELETE FROM atestp1; -- ok
672+
DELETE FROM atestc; -- fail
673+
ERROR: permission denied for relation atestc
674+
UPDATE atestp1 SET f1 = 1; -- ok
675+
UPDATE atestc SET f1 = 1; -- fail
676+
ERROR: permission denied for relation atestc
677+
TRUNCATE atestp1; -- ok
678+
TRUNCATE atestc; -- fail
679+
ERROR: permission denied for relation atestc
659680
-- privileges on functions, languages
660681
-- switch to superuser
661682
\c -

src/test/regress/sql/privileges.sql

+14
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,20 @@ SELECT fy FROM atestp2; -- ok
414414
SELECT atestp2 FROM atestp2; -- ok
415415
SELECT oid FROM atestp2; -- ok
416416

417+
-- child's permissions do not apply when operating on parent
418+
SET SESSION AUTHORIZATION regressuser1;
419+
REVOKE ALL ON atestc FROM regressuser2;
420+
GRANT ALL ON atestp1 TO regressuser2;
421+
SET SESSION AUTHORIZATION regressuser2;
422+
SELECT f2 FROM atestp1; -- ok
423+
SELECT f2 FROM atestc; -- fail
424+
DELETE FROM atestp1; -- ok
425+
DELETE FROM atestc; -- fail
426+
UPDATE atestp1 SET f1 = 1; -- ok
427+
UPDATE atestc SET f1 = 1; -- fail
428+
TRUNCATE atestp1; -- ok
429+
TRUNCATE atestc; -- fail
430+
417431
-- privileges on functions, languages
418432

419433
-- switch to superuser

0 commit comments

Comments
 (0)