From ad6890564c9408c299460decf013c02ce8f44e2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Novotn=C3=BD?= Date: Tue, 30 Mar 2021 11:27:29 +0200 Subject: [PATCH] In security-restricted operations, block enqueue of at-commit user code. Original commit: 0c3185e963d9f9dd0608214f7d732b84aa0888fe Original commit message: Specifically, this blocks DECLARE ... WITH HOLD and firing of deferred triggers within index expressions and materialized view queries. An attacker having permission to create non-temp objects in at least one schema could execute arbitrary SQL functions under the identity of the bootstrap superuser. One can work around the vulnerability by disabling autovacuum and not manually running ANALYZE, CLUSTER, REINDEX, CREATE INDEX, VACUUM FULL, or REFRESH MATERIALIZED VIEW. (Don't restore from pg_dump, since it runs some of those commands.) Plain VACUUM (without FULL) is safe, and all commands are fine when a trusted user owns the target object. Performance may degrade quickly under this workaround, however. Back-patch to 9.5 (all supported versions). --- src/backend/commands/portalcmds.c | 6 ++++++ src/backend/commands/trigger.c | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index e458adfad1..ea1b198dc8 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -27,6 +27,7 @@ #include "commands/portalcmds.h" #include "executor/executor.h" #include "executor/tstoreReceiver.h" +#include "miscadmin.h" #include "tcop/pquery.h" #include "utils/memutils.h" #include "utils/snapmgr.h" @@ -67,6 +68,11 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params, */ if (!(cstmt->options & CURSOR_OPT_HOLD)) RequireTransactionChain(isTopLevel, "DECLARE CURSOR"); + else if (InSecurityRestrictedOperation()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("cannot create a cursor WITH HOLD within security-restricted operation"))); + /* * Create a portal and copy the plan and queryString into its memory. diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 357313dffa..5a0c65fcaa 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -3480,6 +3480,7 @@ afterTriggerMarkEvents(AfterTriggerEventList *events, bool immediate_only) { bool found = false; + bool deferred_found = false; AfterTriggerEvent event; AfterTriggerEventChunk *chunk; @@ -3515,6 +3516,7 @@ afterTriggerMarkEvents(AfterTriggerEventList *events, */ if (defer_it && move_list != NULL) { + deferred_found = true; /* add it to move_list */ afterTriggerAddEvent(move_list, event, evtshared); /* mark original copy "done" so we don't do it again */ @@ -3522,6 +3524,17 @@ afterTriggerMarkEvents(AfterTriggerEventList *events, } } + /* + * We could allow deferred triggers if, before the end of the + * security-restricted operation, we were to verify that a SET CONSTRAINTS + * ... IMMEDIATE has fired all such triggers. For now, don't bother. + */ + if (deferred_found && InSecurityRestrictedOperation()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("cannot fire deferred trigger within security-restricted operation"))); + + return found; } -- 2.26.2