[packages/dovecot-pigeonhole] Backport 2.4.x security fixes; rel 2
arekm
arekm at pld-linux.org
Wed Jul 29 21:46:34 CEST 2026
commit bb3c715894de4111c74f9e5f7dc24916630715e4
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date: Wed Jul 29 21:43:20 2026 +0200
Backport 2.4.x security fixes; rel 2
4 CVEs + unreleased :matches panic fix; distro CVE-2026-40016 dropped
"vp += key_offset;" and broke :matches - restored to match 2.4.
CVE-2025-59032.patch | 29 +++++
CVE-2026-27857.patch | 28 +++++
CVE-2026-27858.patch | 27 +++++
CVE-2026-40016.patch | 252 +++++++++++++++++++++++++++++++++++++++++++
dovecot-pigeonhole-git.patch | 71 ++++++++++++
dovecot-pigeonhole.spec | 17 ++-
6 files changed, 422 insertions(+), 2 deletions(-)
---
diff --git a/dovecot-pigeonhole.spec b/dovecot-pigeonhole.spec
index 13d7946..ab12d34 100644
--- a/dovecot-pigeonhole.spec
+++ b/dovecot-pigeonhole.spec
@@ -7,17 +7,25 @@ Summary: Sieve plugin for dovecot
Summary(pl.UTF-8): Wtyczka Sieve i Managesieve dla dovecota
Name: dovecot-pigeonhole
Version: %{dovecot_series}_%{pigeonhole_version}
-Release: 1
+Release: 2
License: LGPL
Group: Daemons
Source0: https://pigeonhole.dovecot.org/releases/%{dovecot_series}/dovecot-%{dovecot_series}-pigeonhole-%{pigeonhole_version}.tar.gz
# Source0-md5: d2b811648a3e2942736a5e1755bfe872
+# unreleased fix from upstream's release-0.5 branch (dead since 2025-04-10)
+Patch100: %{name}-git.patch
+# 2.3 is EOL upstream; security fixes backported from 2.4.x via Debian/Ubuntu
+Patch0: CVE-2025-59032.patch
+Patch1: CVE-2026-27857.patch
+Patch2: CVE-2026-27858.patch
+Patch3: CVE-2026-40016.patch
URL: http://pigeonhole.dovecot.org/
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: bison
#BuildRequires: dovecot-devel >= 1:%{dovecot_series}
-BuildRequires: dovecot-devel >= 1:2.3.21
+# CVE-2026-27857 changed the imap_parser_create() API in lib-imap
+BuildRequires: dovecot-devel >= 1:2.3.21.1-9
BuildRequires: flex
BuildRequires: libtool
%requires_eq_to dovecot dovecot-devel
@@ -56,6 +64,11 @@ Ten pakiet zawiera demona Manage Sieve dla dovecot.
%prep
%setup -q -n dovecot-%{dovecot_series}-pigeonhole-%{pigeonhole_version}
+%patch -P100 -p1
+%patch -P0 -p1
+%patch -P1 -p1
+%patch -P2 -p1
+%patch -P3 -p1
%build
%configure \
diff --git a/CVE-2025-59032.patch b/CVE-2025-59032.patch
new file mode 100644
index 0000000..7d372f3
--- /dev/null
+++ b/CVE-2025-59032.patch
@@ -0,0 +1,29 @@
+From efb68fac3a9d2d04d38c4ab14dd570cf0c23923c Mon Sep 17 00:00:00 2001
+From: Timo Sirainen <timo.sirainen at open-xchange.com>
+Date: Mon, 22 Dec 2025 22:25:04 +0200
+Subject: [PATCH] managesieve-login: Fix crash when command didn't finish on
+ the first call
+
+---
+ src/managesieve-login/client.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/src/managesieve-login/client.c
++++ b/src/managesieve-login/client.c
+@@ -330,10 +330,12 @@ static bool managesieve_client_input_nex
+ if (args[0].type != MANAGESIEVE_ARG_EOL)
+ ret = -1;
+ }
+- }
+- if (ret > 0) {
++ if (ret > 0)
++ ret = client->cmd->func(client, args);
++ } else {
++ /* Continue unfinished command */
+ i_assert(client->cmd != NULL);
+- ret = client->cmd->func(client, args);
++ ret = client->cmd->func(client, NULL);
+ }
+
+ if (ret != 0)
+
diff --git a/CVE-2026-27857.patch b/CVE-2026-27857.patch
new file mode 100644
index 0000000..89071ed
--- /dev/null
+++ b/CVE-2026-27857.patch
@@ -0,0 +1,28 @@
+From bc91df5f91e8b83c58f138799d39be687271106d Mon Sep 17 00:00:00 2001
+From: Timo Sirainen <timo.sirainen at open-xchange.com>
+Date: Fri, 6 Mar 2026 17:06:45 +0200
+Subject: [PATCH 1/2] plugins: imap-filter-sieve: imap-filter-sieve - Adjust to
+ imap_parser_create() API change
+
+---
+ src/plugins/imap-filter-sieve/cmd-filter-sieve.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/pigeonhole/src/plugins/imap-filter-sieve/cmd-filter-sieve.c b/pigeonhole/src/plugins/imap-filter-sieve/cmd-filter-sieve.c
+index 6965b511..33d6804c 100644
+--- a/src/plugins/imap-filter-sieve/cmd-filter-sieve.c
++++ b/src/plugins/imap-filter-sieve/cmd-filter-sieve.c
+@@ -379,7 +379,8 @@ bool cmd_filter_sieve(struct client_command_context *cmd)
+ asynchronously the same way as APPEND does. */
+ client->input_lock = cmd;
+ ctx->parser = imap_parser_create(client->input, client->output,
+- client->set->imap_max_line_length);
++ client->set->imap_max_line_length,
++ NULL);
+ if (client->set->imap_literal_minus)
+ imap_parser_enable_literal_minus(ctx->parser);
+ o_stream_unset_flush_callback(client->output);
+--
+2.39.5
+
+
diff --git a/CVE-2026-27858.patch b/CVE-2026-27858.patch
new file mode 100644
index 0000000..afacfc2
--- /dev/null
+++ b/CVE-2026-27858.patch
@@ -0,0 +1,27 @@
+From fcb339d39bfb19abf1bb88711112f49c1c589b1a Mon Sep 17 00:00:00 2001
+From: Timo Sirainen <timo.sirainen at open-xchange.com>
+Date: Mon, 2 Mar 2026 14:40:57 +0200
+Subject: [PATCH 2/2] managesieve-login: Verify AUTHENTICATE initial response
+ size isn't too large
+
+This prevents DoSing the managesieve-login by sending an excessively large
+initial response size, which causes a huge memory allocation.
+---
+ src/managesieve-login/client-authenticate.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/src/managesieve-login/client-authenticate.c
++++ b/src/managesieve-login/client-authenticate.c
+@@ -189,6 +189,11 @@ static int managesieve_client_auth_read_
+ if ( i_stream_get_size
+ (msieve_client->auth_response_input, FALSE, &resp_size) <= 0 )
+ resp_size = 0;
++ else if (resp_size > LOGIN_MAX_AUTH_BUF_SIZE) {
++ client_destroy(client,
++ "Authentication response too large");
++ return -1;
++ }
+
+ if (client->auth_response == NULL)
+ client->auth_response = str_new(default_pool, I_MAX(resp_size+1, 256));
+
diff --git a/CVE-2026-40016.patch b/CVE-2026-40016.patch
new file mode 100644
index 0000000..b946ecf
--- /dev/null
+++ b/CVE-2026-40016.patch
@@ -0,0 +1,252 @@
+Backport of:
+
+From 5b0ed9d1034c023d3daf218b6b8656f0cdd383dc Mon Sep 17 00:00:00 2001
+From: Timo Sirainen <timo.sirainen at open-xchange.com>
+Date: Sun, 19 Apr 2026 23:10:29 +0000
+Subject: [PATCH] lib-sieve: Enforce CPU time limit within :contains and
+ :matches matcher loops
+
+The naive O(N*M) substring search in mcht-contains.c and the naive find loop
+in mcht-matches.c can run for hours on a large value (e.g. a message body),
+completely bypassing sieve_max_cpu_time because that limit was only checked
+between bytecode operations.
+
+Expose the active CPU limit via sieve_runtime_cpu_limit_exceeded() and poll
+it every 4096 inner iterations. When the limit is hit the match returns
+SIEVE_EXEC_RESOURCE_LIMIT, matching the existing behavior at the bytecode
+boundary. This is a minimal safety net ahead of switching the matchers to
+algorithms that do not require it.
+---
+ src/lib-sieve/mcht-contains.c | 21 ++++++++++++
+ src/lib-sieve/mcht-matches.c | 57 +++++++++++++++++++++++++++----
+ src/lib-sieve/sieve-interpreter.c | 19 +++++++++++
+ src/lib-sieve/sieve-interpreter.h | 12 +++++++
+ 4 files changed, 103 insertions(+), 6 deletions(-)
+
+PLD: the Debian/Ubuntu backport dropped the "vp += key_offset;" line when it
+rewrote the _string_find() call site, which made :matches search from the wrong
+offset (tests/match-types/matches.svtest test 18 regressed). Restored here to
+match upstream 2.4, which keeps that line.
+
+--- a/src/lib-sieve/mcht-contains.c
++++ b/src/lib-sieve/mcht-contains.c
+@@ -8,6 +8,7 @@
+
+ #include "sieve-match-types.h"
+ #include "sieve-comparators.h"
++#include "sieve-interpreter.h"
+ #include "sieve-match.h"
+
+ #include <string.h>
+@@ -38,7 +39,14 @@
+
+ /* FIXME: Naive substring match implementation. Should switch to more
+ * efficient algorithm if large values need to be searched (e.g. message body).
++ *
++ * The inner loop polls the interpreter CPU time limit periodically so that a
++ * single O(N*M) match on a large value cannot run for many times the
++ * configured sieve_max_cpu_time (which is otherwise only checked between
++ * bytecode operations).
+ */
++#define SIEVE_CONTAINS_CPU_CHECK_INTERVAL 4096
++
+ static int mcht_contains_match_key
+ (struct sieve_match_context *mctx, const char *val, size_t val_size,
+ const char *key, size_t key_size)
+@@ -48,6 +56,7 @@
+ const char *kend = (const char *) key + key_size;
+ const char *vp = val;
+ const char *kp = key;
++ unsigned int counter = 0;
+
+ if ( val_size == 0 )
+ return ( key_size == 0 ? 1 : 0 );
+@@ -58,6 +67,18 @@
+ while ( (vp < vend) && (kp < kend) ) {
+ if ( !cmp->def->char_match(cmp, &vp, vend, &kp, kend) )
+ vp++;
++
++ if ( ++counter >= SIEVE_CONTAINS_CPU_CHECK_INTERVAL ) {
++ counter = 0;
++ if ( sieve_runtime_cpu_limit_exceeded(mctx->runenv) ) {
++ sieve_runtime_error(
++ mctx->runenv, NULL,
++ "execution exceeded CPU time limit");
++ mctx->exec_status =
++ SIEVE_EXEC_RESOURCE_LIMIT;
++ return -1;
++ }
++ }
+ }
+
+ return ( kp == kend ? 1 : 0 );
+--- a/src/lib-sieve/mcht-matches.c
++++ b/src/lib-sieve/mcht-matches.c
+@@ -9,6 +9,7 @@
+
+ #include "sieve-match-types.h"
+ #include "sieve-comparators.h"
++#include "sieve-interpreter.h"
+ #include "sieve-match.h"
+
+ #include <string.h>
+@@ -46,16 +47,39 @@
+ #endif
+
+ /* FIXME: Naive implementation, substitute this with dovecot src/lib/str-find.c
++ *
++ * The inner loop polls the interpreter CPU time limit periodically so that a
++ * single O(N*M) search on a large value cannot run for many times the
++ * configured sieve_max_cpu_time. Returns 1 on match, 0 on exhaustion, or -1
++ * when the CPU time limit was exceeded (mctx->exec_status is set).
+ */
+-static inline bool _string_find(const struct sieve_comparator *cmp,
+- const char **valp, const char *vend, const char **keyp, const char *kend)
++#define SIEVE_MATCHES_CPU_CHECK_INTERVAL 4096
++
++static int
++_string_find(struct sieve_match_context *mctx,
++ const struct sieve_comparator *cmp,
++ const char **valp, const char *vend,
++ const char **keyp, const char *kend,
++ unsigned int *counter)
+ {
+ while ( (*valp < vend) && (*keyp < kend) ) {
+ if ( !cmp->def->char_match(cmp, valp, vend, keyp, kend) )
+ (*valp)++;
++
++ if (++(*counter) >= SIEVE_MATCHES_CPU_CHECK_INTERVAL) {
++ *counter = 0;
++ if (sieve_runtime_cpu_limit_exceeded(mctx->runenv)) {
++ sieve_runtime_error(
++ mctx->runenv, NULL,
++ "execution exceeded CPU time limit");
++ mctx->exec_status =
++ SIEVE_EXEC_RESOURCE_LIMIT;
++ return -1;
++ }
++ }
+ }
+
+- return (*keyp == kend);
++ return (*keyp == kend ? 1 : 0);
+ }
+
+ static char _scan_key_section
+@@ -93,6 +117,7 @@
+ char wcard = '\0'; /* Current wildcard */
+ char next_wcard = '\0'; /* Next widlcard */
+ unsigned int key_offset = 0;
++ unsigned int counter = 0;
+
+ if ( cmp->def == NULL || cmp->def->char_match == NULL )
+ return 0;
+@@ -134,6 +159,19 @@
+ while (kp < kend && vp < vend ) {
+ const char *needle, *nend;
+
++ if (++counter >= SIEVE_MATCHES_CPU_CHECK_INTERVAL) {
++ counter = 0;
++ if (sieve_runtime_cpu_limit_exceeded(mctx->runenv)) {
++ sieve_runtime_error(
++ mctx->runenv, NULL,
++ "execution exceeded CPU time limit");
++ mctx->exec_status =
++ SIEVE_EXEC_RESOURCE_LIMIT;
++ sieve_match_values_abort(&mvalues);
++ return -1;
++ }
++ }
++
+ if ( !backtrack ) {
+ /* Search the next '*' wildcard in the key string */
+
+@@ -268,7 +306,17 @@
+
+ /* Match may happen at any offset (>= key offset): find substring */
+ vp += key_offset;
+- if ( (vp >= vend) || !_string_find(cmp, &vp, vend, &needle, nend) ) {
++ if (vp >= vend) {
++ debug_printf(" failed to find needle at an offset\n");
++ break;
++ }
++ int fres = _string_find(mctx, cmp, &vp, vend,
++ &needle, nend, &counter);
++ if (fres < 0) {
++ sieve_match_values_abort(&mvalues);
++ return -1;
++ }
++ if (fres == 0) {
+ debug_printf(" failed to find needle at an offset\n");
+ break;
+ }
+--- a/src/lib-sieve/sieve-interpreter.c
++++ b/src/lib-sieve/sieve-interpreter.c
+@@ -85,6 +85,13 @@
+ struct sieve_runtime_trace trace;
+ struct sieve_resource_usage rusage;
+
++ /* CPU time limit for the current sieve_interpreter_continue() call;
++ NULL when no limit is configured or not currently executing. Exposed
++ via sieve_runtime_cpu_limit_exceeded() so long-running runtime code
++ can enforce the limit without waiting for the next bytecode
++ boundary. */
++ struct cpu_limit *climit;
++
+ /* Current operation */
+ struct sieve_operation oprtn;
+
+@@ -362,6 +369,15 @@
+ return interp->runenv.exec_env->svinst;
+ }
+
++bool sieve_runtime_cpu_limit_exceeded(const struct sieve_runtime_env *renv)
++{
++ struct sieve_interpreter *interp = renv->interp;
++
++ if (interp->climit == NULL)
++ return FALSE;
++ return cpu_limit_exceeded(interp->climit);
++}
++
+ /* Do not use this function for normal sieve extensions. This is intended for
+ * the testsuite only.
+ */
+@@ -939,6 +955,7 @@
+ climit = cpu_limit_init(svinst->max_cpu_time_secs,
+ CPU_LIMIT_TYPE_USER);
+ }
++ interp->climit = climit;
+
+ while (ret == SIEVE_EXEC_OK && !interp->interrupted &&
+ *address < sieve_binary_block_get_size(renv->sblock)) {
+@@ -959,6 +976,8 @@
+ ret = sieve_interpreter_operation_execute(interp);
+ }
+
++ interp->climit = NULL;
++
+ if (climit != NULL) {
+ sieve_resource_usage_init(&rusage);
+ rusage.cpu_time_msecs =
+--- a/src/lib-sieve/sieve-interpreter.h
++++ b/src/lib-sieve/sieve-interpreter.h
+@@ -165,6 +165,18 @@
+ struct sieve_result *result);
+
+ /*
++ * CPU limit
++ */
++
++/* Returns TRUE if the current interpreter execution has exceeded its CPU
++ time limit (sieve_max_cpu_time). Callable from within long-running runtime
++ code (e.g. matcher inner loops) so that limit enforcement is not deferred
++ until the next bytecode boundary. Returns FALSE if no limit is active or
++ no execution is currently in progress. Cheap: does not call getrusage()
++ on each invocation. */
++bool sieve_runtime_cpu_limit_exceeded(const struct sieve_runtime_env *renv);
++
++/*
+ * Error handling
+ */
+
diff --git a/dovecot-pigeonhole-git.patch b/dovecot-pigeonhole-git.patch
new file mode 100644
index 0000000..5ffab2d
--- /dev/null
+++ b/dovecot-pigeonhole-git.patch
@@ -0,0 +1,71 @@
+From 5055e3afd38fbdaff422fd814b5c458a67bbd2b8 Mon Sep 17 00:00:00 2001
+From: Stephan Bosch <stephan.bosch at open-xchange.com>
+Date: Thu, 10 Apr 2025 01:43:08 +0200
+Subject: [PATCH] lib-sieve: mcht-matches - Fix panics occurring with initial
+ *? wildcards with variables enabled
+
+Panic: file bits.c: line 17 (nearest_power): assertion failed:
+(num <= ((size_t)1 << (CHAR_BIT*sizeof(size_t) - 1)))
+
+Rebased onto 0.5.21.1, which predates the upstream mcht-matches.c reformat.
+--- a/src/lib-sieve/mcht-matches.c
++++ b/src/lib-sieve/mcht-matches.c
+@@ -212,9 +212,6 @@
+ qend = vp;
+ qp = vp - key_offset;
+
+- if ( mvalues != NULL )
+- str_append_data(mvalue, pvp, qp-pvp);
+-
+ /* Compare needle to end of value string */
+ if ( !cmp->def->char_match(cmp, &vp, vend, &needle, nend) ) {
+ debug_printf(" match at end failed\n");
+@@ -223,6 +220,9 @@
+
+ /* Add match values */
+ if ( mvalues != NULL ) {
++ i_assert(qp >= pvp);
++ str_append_data(mvalue, pvp, qp-pvp);
++
+ /* Append '*' match value */
+ sieve_match_values_add(mvalues, mvalue);
+
+--- a/tests/extensions/variables/match.svtest
++++ b/tests/extensions/variables/match.svtest
+@@ -363,3 +363,36 @@
+ test_fail "incorrect match values: ${1}${2}";
+ }
+ }
++
++test "Initial *??" {
++ if string :matches "foo1234 at example.com" ["*??@foobar.example.com"] {
++ test_fail "Unexpectedly matched";
++ }
++ if not string :matches "foo1234 at foobar.example.com" ["*??@foobar.example.com"] {
++ test_fail "Failed to match";
++ }
++ if not string "${1}" "foo12" {
++ test_fail "Match value 1 is wrong: `${1}`";
++ }
++ if not string "${2}" "3" {
++ test_fail "Match value 1 is wrong: `${2}`";
++ }
++ if not string "${3}" "4" {
++ test_fail "Match value 1 is wrong: `${3}`";
++ }
++}
++
++test "Intiial *?" {
++ if string :matches "d at example.jp" ["*?@example.com"] {
++ test_fail "Unexpectedly matched";
++ }
++ if not string :matches "d at example.com" ["*?@example.com"] {
++ test_fail "Failed to match";
++ }
++ if not string "${1}" "" {
++ test_fail "Match value 1 is wrong: `${1}`";
++ }
++ if not string "${2}" "d" {
++ test_fail "Match value 1 is wrong: `${2}`";
++ }
++}
================================================================
---- gitweb:
http://git.pld-linux.org/gitweb.cgi/packages/dovecot-pigeonhole.git/commitdiff/bb3c715894de4111c74f9e5f7dc24916630715e4
More information about the pld-cvs-commit
mailing list