Make inherited LOCK TABLE perform access permission checks on parent table only.
Previously, LOCK TABLE 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 LOCK TABLE so that it does not check the permission on children tables. This patch is applied only in the master branch. We decided not to back-patch because it's not hard to imagine that there are some applications expecting the old behavior and the change breaks their security. Author: Amit Langote Reviewed-by: Fujii Masao Discussion: https://postgr.es/m/CAHGQGwE+GauyG7POtRfRwwthAGwTjPQYdFHR97+LzA4RHGnJxA@mail.gmail.com
This commit is contained in:
parent
958f9fb98d
commit
b7e51b350c
@ -28,7 +28,7 @@
|
|||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, Oid userid);
|
static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait);
|
||||||
static AclResult LockTableAclCheck(Oid relid, LOCKMODE lockmode, Oid userid);
|
static AclResult LockTableAclCheck(Oid relid, LOCKMODE lockmode, Oid userid);
|
||||||
static void RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid,
|
static void RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid,
|
||||||
Oid oldrelid, void *arg);
|
Oid oldrelid, void *arg);
|
||||||
@ -59,7 +59,7 @@ LockTableCommand(LockStmt *lockstmt)
|
|||||||
if (get_rel_relkind(reloid) == RELKIND_VIEW)
|
if (get_rel_relkind(reloid) == RELKIND_VIEW)
|
||||||
LockViewRecurse(reloid, lockstmt->mode, lockstmt->nowait, NIL);
|
LockViewRecurse(reloid, lockstmt->mode, lockstmt->nowait, NIL);
|
||||||
else if (recurse)
|
else if (recurse)
|
||||||
LockTableRecurse(reloid, lockstmt->mode, lockstmt->nowait, GetUserId());
|
LockTableRecurse(reloid, lockstmt->mode, lockstmt->nowait);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,35 +108,26 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
|
|||||||
/*
|
/*
|
||||||
* Apply LOCK TABLE recursively over an inheritance tree
|
* Apply LOCK TABLE recursively over an inheritance tree
|
||||||
*
|
*
|
||||||
* We use find_inheritance_children not find_all_inheritors to avoid taking
|
* This doesn't check permission to perform LOCK TABLE on the child tables,
|
||||||
* locks far in advance of checking privileges. This means we'll visit
|
* because getting here means that the user has permission to lock the
|
||||||
* multiply-inheriting children more than once, but that's no problem.
|
* parent which is enough.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, Oid userid)
|
LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait)
|
||||||
{
|
{
|
||||||
List *children;
|
List *children;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
children = find_inheritance_children(reloid, NoLock);
|
children = find_all_inheritors(reloid, NoLock, NULL);
|
||||||
|
|
||||||
foreach(lc, children)
|
foreach(lc, children)
|
||||||
{
|
{
|
||||||
Oid childreloid = lfirst_oid(lc);
|
Oid childreloid = lfirst_oid(lc);
|
||||||
AclResult aclresult;
|
|
||||||
|
|
||||||
/* Check permissions before acquiring the lock. */
|
/* Parent already locked. */
|
||||||
aclresult = LockTableAclCheck(childreloid, lockmode, userid);
|
if (childreloid == reloid)
|
||||||
if (aclresult != ACLCHECK_OK)
|
continue;
|
||||||
{
|
|
||||||
char *relname = get_rel_name(childreloid);
|
|
||||||
|
|
||||||
if (!relname)
|
|
||||||
continue; /* child concurrently dropped, just skip it */
|
|
||||||
aclcheck_error(aclresult, get_relkind_objtype(get_rel_relkind(childreloid)), relname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We have enough rights to lock the relation; do so. */
|
|
||||||
if (!nowait)
|
if (!nowait)
|
||||||
LockRelationOid(childreloid, lockmode);
|
LockRelationOid(childreloid, lockmode);
|
||||||
else if (!ConditionalLockRelationOid(childreloid, lockmode))
|
else if (!ConditionalLockRelationOid(childreloid, lockmode))
|
||||||
@ -162,8 +153,6 @@ LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, Oid userid)
|
|||||||
UnlockRelationOid(childreloid, lockmode);
|
UnlockRelationOid(childreloid, lockmode);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LockTableRecurse(childreloid, lockmode, nowait, userid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +230,7 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context)
|
|||||||
if (relkind == RELKIND_VIEW)
|
if (relkind == RELKIND_VIEW)
|
||||||
LockViewRecurse(relid, context->lockmode, context->nowait, context->ancestor_views);
|
LockViewRecurse(relid, context->lockmode, context->nowait, context->ancestor_views);
|
||||||
else if (rte->inh)
|
else if (rte->inh)
|
||||||
LockTableRecurse(relid, context->lockmode, context->nowait, context->viewowner);
|
LockTableRecurse(relid, context->lockmode, context->nowait);
|
||||||
}
|
}
|
||||||
|
|
||||||
return query_tree_walker(query,
|
return query_tree_walker(query,
|
||||||
|
@ -138,13 +138,17 @@ CREATE TABLE lock_tbl3 () INHERITS (lock_tbl2);
|
|||||||
BEGIN TRANSACTION;
|
BEGIN TRANSACTION;
|
||||||
LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE;
|
LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE;
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
-- Verify that we can't lock a child table just because we have permission
|
-- Child tables are locked without granting explicit permission to do so as
|
||||||
-- on the parent, but that we can lock the parent only.
|
-- long as we have permission to lock the parent.
|
||||||
GRANT UPDATE ON TABLE lock_tbl1 TO regress_rol_lock1;
|
GRANT UPDATE ON TABLE lock_tbl1 TO regress_rol_lock1;
|
||||||
SET ROLE regress_rol_lock1;
|
SET ROLE regress_rol_lock1;
|
||||||
|
-- fail when child locked directly
|
||||||
|
BEGIN;
|
||||||
|
LOCK TABLE lock_tbl2;
|
||||||
|
ERROR: permission denied for table lock_tbl2
|
||||||
|
ROLLBACK;
|
||||||
BEGIN;
|
BEGIN;
|
||||||
LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE;
|
LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE;
|
||||||
ERROR: permission denied for table lock_tbl2
|
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
BEGIN;
|
BEGIN;
|
||||||
LOCK TABLE ONLY lock_tbl1;
|
LOCK TABLE ONLY lock_tbl1;
|
||||||
|
@ -716,6 +716,13 @@ ERROR: permission denied for table atestc
|
|||||||
TRUNCATE atestp1; -- ok
|
TRUNCATE atestp1; -- ok
|
||||||
TRUNCATE atestc; -- fail
|
TRUNCATE atestc; -- fail
|
||||||
ERROR: permission denied for table atestc
|
ERROR: permission denied for table atestc
|
||||||
|
BEGIN;
|
||||||
|
LOCK atestp1;
|
||||||
|
END;
|
||||||
|
BEGIN;
|
||||||
|
LOCK atestc;
|
||||||
|
ERROR: permission denied for table atestc
|
||||||
|
END;
|
||||||
-- privileges on functions, languages
|
-- privileges on functions, languages
|
||||||
-- switch to superuser
|
-- switch to superuser
|
||||||
\c -
|
\c -
|
||||||
|
@ -101,10 +101,14 @@ BEGIN TRANSACTION;
|
|||||||
LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE;
|
LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE;
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
|
|
||||||
-- Verify that we can't lock a child table just because we have permission
|
-- Child tables are locked without granting explicit permission to do so as
|
||||||
-- on the parent, but that we can lock the parent only.
|
-- long as we have permission to lock the parent.
|
||||||
GRANT UPDATE ON TABLE lock_tbl1 TO regress_rol_lock1;
|
GRANT UPDATE ON TABLE lock_tbl1 TO regress_rol_lock1;
|
||||||
SET ROLE regress_rol_lock1;
|
SET ROLE regress_rol_lock1;
|
||||||
|
-- fail when child locked directly
|
||||||
|
BEGIN;
|
||||||
|
LOCK TABLE lock_tbl2;
|
||||||
|
ROLLBACK;
|
||||||
BEGIN;
|
BEGIN;
|
||||||
LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE;
|
LOCK TABLE lock_tbl1 * IN ACCESS EXCLUSIVE MODE;
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
|
@ -459,6 +459,12 @@ UPDATE atestp1 SET f1 = 1; -- ok
|
|||||||
UPDATE atestc SET f1 = 1; -- fail
|
UPDATE atestc SET f1 = 1; -- fail
|
||||||
TRUNCATE atestp1; -- ok
|
TRUNCATE atestp1; -- ok
|
||||||
TRUNCATE atestc; -- fail
|
TRUNCATE atestc; -- fail
|
||||||
|
BEGIN;
|
||||||
|
LOCK atestp1;
|
||||||
|
END;
|
||||||
|
BEGIN;
|
||||||
|
LOCK atestc;
|
||||||
|
END;
|
||||||
|
|
||||||
-- privileges on functions, languages
|
-- privileges on functions, languages
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user