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

Lines changed: 49 additions & 18 deletions
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

Lines changed: 21 additions & 0 deletions
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

Lines changed: 14 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy