aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorNoah Misch <noah@leadboat.com>2020-11-09 07:32:09 -0800
committerNoah Misch <noah@leadboat.com>2020-11-09 07:32:12 -0800
commitac8f6243cb002f3386322fb231a0c5daa637941d (patch)
treeb18cf3d0828fb444301c7f91d3a9372b578813f3 /src/backend
parenta98b46194434927b8402b08ac0edfa6408c7e823 (diff)
downloadpostgresql-ac8f6243cb002f3386322fb231a0c5daa637941d.tar.gz
postgresql-ac8f6243cb002f3386322fb231a0c5daa637941d.zip
In security-restricted operations, block enqueue of at-commit user code.
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). Reviewed by Robert Haas. Reported by Etienne Stalmans. Security: CVE-2020-25695
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/access/transam/xact.c13
-rw-r--r--src/backend/commands/portalcmds.c5
-rw-r--r--src/backend/commands/trigger.c12
3 files changed, 24 insertions, 6 deletions
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index df4f538ce42..e41063474a7 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -2065,9 +2065,10 @@ CommitTransaction(void)
/*
* Do pre-commit processing that involves calling user-defined code, such
- * as triggers. Since closing cursors could queue trigger actions,
- * triggers could open cursors, etc, we have to keep looping until there's
- * nothing left to do.
+ * as triggers. SECURITY_RESTRICTED_OPERATION contexts must not queue an
+ * action that would run here, because that would bypass the sandbox.
+ * Since closing cursors could queue trigger actions, triggers could open
+ * cursors, etc, we have to keep looping until there's nothing left to do.
*/
for (;;)
{
@@ -2085,9 +2086,6 @@ CommitTransaction(void)
break;
}
- CallXactCallbacks(is_parallel_worker ? XACT_EVENT_PARALLEL_PRE_COMMIT
- : XACT_EVENT_PRE_COMMIT);
-
/*
* The remaining actions cannot call any user-defined code, so it's safe
* to start shutting down within-transaction services. But note that most
@@ -2095,6 +2093,9 @@ CommitTransaction(void)
* the transaction-abort path.
*/
+ CallXactCallbacks(is_parallel_worker ? XACT_EVENT_PARALLEL_PRE_COMMIT
+ : XACT_EVENT_PRE_COMMIT);
+
/* If we might have parallel workers, clean them up now. */
if (IsInParallelMode())
AtEOXact_Parallel(true);
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 83f9959d54c..8e1ca74faef 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 "rewrite/rewriteHandler.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
@@ -64,6 +65,10 @@ PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params,
*/
if (!(cstmt->options & CURSOR_OPT_HOLD))
RequireTransactionBlock(isTopLevel, "DECLARE CURSOR");
+ else if (InSecurityRestrictedOperation())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("cannot create a cursor WITH HOLD within security-restricted operation")));
/*
* Parse analysis was done already, but we still have to run the rule
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index d85c053a14a..5d26f998fac 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -4383,6 +4383,7 @@ afterTriggerMarkEvents(AfterTriggerEventList *events,
bool immediate_only)
{
bool found = false;
+ bool deferred_found = false;
AfterTriggerEvent event;
AfterTriggerEventChunk *chunk;
@@ -4418,6 +4419,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 */
@@ -4425,6 +4427,16 @@ 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;
}