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