Version in base suite: 2.4.1+dfsg1-6+deb13u3 Base version: dovecot_2.4.1+dfsg1-6+deb13u3 Target version: dovecot_2.4.1+dfsg1-6+deb13u4 Base file: /srv/ftp-master.debian.org/ftp/pool/main/d/dovecot/dovecot_2.4.1+dfsg1-6+deb13u3.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/d/dovecot/dovecot_2.4.1+dfsg1-6+deb13u4.dsc changelog | 20 +++ dovecot-core.examples | 1 patches/CVE-2025-59028.patch | 40 ++++++ patches/CVE-2025-59031.patch | 137 +++++++++++++++++++++ patches/CVE-2025-59032.patch | 30 ++++ patches/CVE-2026-24031-27860-1.patch | 39 +++++ patches/CVE-2026-24031-27860-2.patch | 113 +++++++++++++++++ patches/CVE-2026-24031-27860-3.patch | 32 ++++ patches/CVE-2026-24031-27860-4.patch | 64 +++++++++ patches/CVE-2026-24031-27860-5.patch | 224 ++++++++++++++++++++++++++++++++++ patches/CVE-2026-24031-27860-6.patch | 36 +++++ patches/CVE-2026-24031-27860-7.patch | 37 +++++ patches/CVE-2026-24031-27860-8.patch | 60 +++++++++ patches/CVE-2026-27855-1.patch | 21 +++ patches/CVE-2026-27855-2.patch | 92 ++++++++++++++ patches/CVE-2026-27855-3.patch | 44 ++++++ patches/CVE-2026-27855-4.patch | 30 ++++ patches/CVE-2026-27856-1.patch | 54 ++++++++ patches/CVE-2026-27856-2.patch | 54 ++++++++ patches/CVE-2026-27856-3.patch | 26 +++ patches/CVE-2026-27857-1.patch | 27 ++++ patches/CVE-2026-27857-2.patch | 228 +++++++++++++++++++++++++++++++++++ patches/CVE-2026-27857-3.patch | 154 +++++++++++++++++++++++ patches/CVE-2026-27857-4.patch | 77 +++++++++++ patches/CVE-2026-27857-5.patch | 53 ++++++++ patches/CVE-2026-27858.patch | 31 ++++ patches/CVE-2026-27859.patch | 100 +++++++++++++++ patches/series | 25 +++ rules | 1 29 files changed, 1848 insertions(+), 2 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmplg2q_3mq/dovecot_2.4.1+dfsg1-6+deb13u3.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmplg2q_3mq/dovecot_2.4.1+dfsg1-6+deb13u4.dsc: no acceptable signature found diff -Nru dovecot-2.4.1+dfsg1/debian/changelog dovecot-2.4.1+dfsg1/debian/changelog --- dovecot-2.4.1+dfsg1/debian/changelog 2026-03-06 14:36:28.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/changelog 2026-03-31 19:07:17.000000000 +0000 @@ -1,3 +1,23 @@ +dovecot (1:2.4.1+dfsg1-6+deb13u4) trixie-security; urgency=medium + + * [bc29057] CVE-2025-59028: auth: Don't disconnect auth client when + invalid base64 SASL input is received + * [fee7a9a] CVE-2025-59031: stop shipping the decode2text shell script + * [9a4442e] CVE-2025-59032: managesieve-login: Fix crash when command + didn't finish on the first call + * [2711b3e] CVE-2026-24031, CVE-2026-27860: auth: fix ldap and sql + injection + * [d30f1c3] CVE-2026-27855: fix OTP authentication reply vulnerability + * [e1b0ff7] CVE-2026-27856: doveadm: fix timing oracle attack + * [b8a69bf] CVE-2026-27857: fix resource exhaustion DoS in NOOP command + parsing + * [85dd068] CVE-2026-27858: fix pre-authentication managesieve memory + consumption issue + * [880e332] CVE-2026-27859: fix uncontrolled resource allocation when + delivering specially crafted email messages + + -- Noah Meyerhans Tue, 31 Mar 2026 15:07:17 -0400 + dovecot (1:2.4.1+dfsg1-6+deb13u3) trixie; urgency=medium * [1186296] import upstream fix for possible crash in ldap userdb diff -Nru dovecot-2.4.1+dfsg1/debian/dovecot-core.examples dovecot-2.4.1+dfsg1/debian/dovecot-core.examples --- dovecot-2.4.1+dfsg1/debian/dovecot-core.examples 2025-11-13 14:48:28.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/dovecot-core.examples 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -src/plugins/fts/decode2text.sh diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2025-59028.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2025-59028.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2025-59028.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2025-59028.patch 2026-03-31 18:21:37.000000000 +0000 @@ -0,0 +1,40 @@ +From dae1a1b84115b2a6146afd9a51e17137264caa0d Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Tue, 4 Nov 2025 11:34:30 +0200 +Subject: [PATCH 01/24] auth: Don't disconnect auth client when invalid base64 + SASL input is received + +The base64 input comes from untrusted client. It shouldn't cause the auth +client to disconnect, which causes other concurrent logins to be aborted. + +Broken by 1486c30e191ff079bfa78e7950173bb33d8073d9 +--- + src/auth/auth-request-handler.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +Index: dovecot/src/auth/auth-request-handler.c +=================================================================== +--- dovecot.orig/src/auth/auth-request-handler.c ++++ dovecot/src/auth/auth-request-handler.c +@@ -715,6 +715,9 @@ int auth_request_handler_auth_begin(stru + auth_request_handler_auth_fail_code(handler, request, + AUTH_CLIENT_FAIL_CODE_INVALID_BASE64, + "Invalid base64 data in initial response"); ++ /* The base64 input came from untrusted client. It's ++ an expected auth failure, so don't disconnect the ++ auth client. */ + return 1; + } + initial_resp_data = +@@ -787,7 +790,10 @@ int auth_request_handler_auth_continue(s + auth_request_handler_auth_fail_code(handler, request, + AUTH_CLIENT_FAIL_CODE_INVALID_BASE64, + "Invalid base64 data in continued response"); +- return -1; ++ /* The base64 input came from untrusted client. It's ++ an expected auth failure, so don't disconnect the ++ auth client. */ ++ return 1; + } + } + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2025-59031.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2025-59031.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2025-59031.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2025-59031.patch 2026-03-31 18:25:27.000000000 +0000 @@ -0,0 +1,137 @@ +From 089edc7750160bf224011c015347db1bdea435e3 Mon Sep 17 00:00:00 2001 +From: Aki Tuomi +Date: Thu, 8 Jan 2026 08:51:59 +0200 +Subject: [PATCH 02/24] fts: Remove decode2text.sh + +The script is flawed and not fit for production use, should +recommend writing your own script, or using Apache Tika. +--- + src/plugins/fts/Makefile.am | 3 - + src/plugins/fts/decode2text.sh | 105 --------------------------------- + 2 files changed, 108 deletions(-) + delete mode 100755 src/plugins/fts/decode2text.sh + +Index: dovecot/src/plugins/fts/Makefile.am +=================================================================== +--- dovecot.orig/src/plugins/fts/Makefile.am ++++ dovecot/src/plugins/fts/Makefile.am +@@ -65,9 +65,6 @@ xml2text_CPPFLAGS = $(AM_CPPFLAGS) $(BIN + xml2text_LDADD = $(LIBDOVECOT) $(BINARY_LDFLAGS) + xml2text_DEPENDENCIES = $(module_LTLIBRARIES) $(LIBDOVECOT_DEPS) + +-pkglibexec_SCRIPTS = decode2text.sh +-EXTRA_DIST = $(pkglibexec_SCRIPTS) +- + doveadm_module_LTLIBRARIES = \ + lib20_doveadm_fts_plugin.la + +Index: dovecot/src/plugins/fts/decode2text.sh +=================================================================== +--- dovecot.orig/src/plugins/fts/decode2text.sh ++++ /dev/null +@@ -1,105 +0,0 @@ +-#!/bin/sh +- +-# Example attachment decoder script. The attachment comes from stdin, and +-# the script is expected to output UTF-8 data to stdout. (If the output isn't +-# UTF-8, everything except valid UTF-8 sequences are dropped from it.) +- +-# The attachment decoding is enabled by setting: +-# +-# plugin { +-# fts_decoder = decode2text +-# } +-# service decode2text { +-# executable = script /usr/local/libexec/dovecot/decode2text.sh +-# user = dovecot +-# unix_listener decode2text { +-# mode = 0666 +-# } +-# } +- +-libexec_dir=`dirname $0` +-content_type=$1 +- +-# The second parameter is the format's filename extension, which is used when +-# found from a filename of application/octet-stream. You can also add more +-# extensions by giving more parameters. +-formats='application/pdf pdf +-application/x-pdf pdf +-application/msword doc +-application/mspowerpoint ppt +-application/vnd.ms-powerpoint ppt +-application/ms-excel xls +-application/x-msexcel xls +-application/vnd.ms-excel xls +-application/vnd.openxmlformats-officedocument.wordprocessingml.document docx +-application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx +-application/vnd.openxmlformats-officedocument.presentationml.presentation pptx +-application/vnd.oasis.opendocument.text odt +-application/vnd.oasis.opendocument.spreadsheet ods +-application/vnd.oasis.opendocument.presentation odp +-' +- +-if [ "$content_type" = "" ]; then +- echo "$formats" +- exit 0 +-fi +- +-fmt=`echo "$formats" | grep -w "^$content_type" | cut -d ' ' -f 2` +-if [ "$fmt" = "" ]; then +- echo "Content-Type: $content_type not supported" >&2 +- exit 1 +-fi +- +-# most decoders can't handle stdin directly, so write the attachment +-# to a temp file +-path=`mktemp` +-trap "rm -f $path" 0 1 2 3 14 15 +-cat > $path +- +-xmlunzip() { +- name=$1 +- +- tempdir=`mktemp -d` +- if [ "$tempdir" = "" ]; then +- exit 1 +- fi +- trap "rm -rf $path $tempdir" 0 1 2 3 14 15 +- cd $tempdir || exit 1 +- unzip -q "$path" 2>/dev/null || exit 0 +- find . -name "$name" -print0 | xargs -0 cat | +- $libexec_dir/xml2text +-} +- +-wait_timeout() { +- childpid=$! +- trap "kill -9 $childpid; rm -f $path" 1 2 3 14 15 +- wait $childpid +-} +- +-LANG=en_US.UTF-8 +-export LANG +-if [ $fmt = "pdf" ]; then +- /usr/bin/pdftotext $path - 2>/dev/null& +- wait_timeout 2>/dev/null +-elif [ $fmt = "doc" ]; then +- (/usr/bin/catdoc $path; true) 2>/dev/null& +- wait_timeout 2>/dev/null +-elif [ $fmt = "ppt" ]; then +- (/usr/bin/catppt $path; true) 2>/dev/null& +- wait_timeout 2>/dev/null +-elif [ $fmt = "xls" ]; then +- (/usr/bin/xls2csv $path; true) 2>/dev/null& +- wait_timeout 2>/dev/null +-elif [ $fmt = "odt" -o $fmt = "ods" -o $fmt = "odp" ]; then +- xmlunzip "content.xml" +-elif [ $fmt = "docx" ]; then +- xmlunzip "document.xml" +-elif [ $fmt = "xlsx" ]; then +- xmlunzip "sharedStrings.xml" +-elif [ $fmt = "pptx" ]; then +- xmlunzip "slide*.xml" +-else +- echo "Buggy decoder script: $fmt not handled" >&2 +- exit 1 +-fi +-exit 0 diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2025-59032.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2025-59032.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2025-59032.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2025-59032.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,30 @@ +From efb68fac3a9d2d04d38c4ab14dd570cf0c23923c Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +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(-) + +Index: dovecot/pigeonhole/src/managesieve-login/client.c +=================================================================== +--- dovecot.orig/pigeonhole/src/managesieve-login/client.c ++++ dovecot/pigeonhole/src/managesieve-login/client.c +@@ -336,10 +336,12 @@ static bool managesieve_client_input_nex + if (args[0].type != MANAGESIEVE_ARG_EOL) + ret = -1; + } +- } +- if (ret > 0) { ++ if (ret > 0) ++ ret = msieve_client->cmd->func(msieve_client, args); ++ } else { ++ /* Continue unfinished command */ + i_assert(msieve_client->cmd != NULL); +- ret = msieve_client->cmd->func(msieve_client, args); ++ ret = msieve_client->cmd->func(msieve_client, NULL); + } + + if (ret != 0) diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-1.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-1.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-1.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,39 @@ +From 466023fc20b05eb6a79d85b0d5efaa45a81ca48d Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Wed, 25 Feb 2026 09:33:25 +0200 +Subject: [PATCH 04/24] auth: Make struct settings_get_params params const + +--- + src/auth/passdb-sql.c | 2 +- + src/auth/userdb-sql.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/auth/passdb-sql.c b/src/auth/passdb-sql.c +index db1990dc47..f3682d4926 100644 +--- a/src/auth/passdb-sql.c ++++ b/src/auth/passdb-sql.c +@@ -182,7 +182,7 @@ static void sql_lookup_pass(struct passdb_sql_request *sql_request) + const struct passdb_sql_settings *set; + const char *error; + +- struct settings_get_params params = { ++ const struct settings_get_params params = { + .escape_func = passdb_sql_escape, + .escape_context = module->db, + }; +diff --git a/src/auth/userdb-sql.c b/src/auth/userdb-sql.c +index f97fe5e316..349f61c49e 100644 +--- a/src/auth/userdb-sql.c ++++ b/src/auth/userdb-sql.c +@@ -128,7 +128,7 @@ static void userdb_sql_lookup(struct auth_request *auth_request, + const struct userdb_sql_settings *set; + const char *error; + +- struct settings_get_params params = { ++ const struct settings_get_params params = { + .escape_func = userdb_sql_escape, + .escape_context = module->db, + }; +-- +2.39.5 + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-2.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-2.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-2.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,113 @@ +From 531c0801f0431eadf91383dab25aa9a8ede0d22f Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Fri, 20 Feb 2026 18:37:38 +0200 +Subject: [PATCH 05/24] auth: passdb/userdb ldap - Fix escaping ldap filter, + base and bind_userdn + +Broken by c2ccdab8d09dec65753ee42366f48d53d7f47cfd +--- + src/auth/passdb-ldap.c | 23 +++++++++++++++++------ + src/auth/userdb-ldap.c | 23 +++++++++++++++++------ + 2 files changed, 34 insertions(+), 12 deletions(-) + +Index: dovecot/src/auth/passdb-ldap.c +=================================================================== +--- dovecot.orig/src/auth/passdb-ldap.c ++++ dovecot/src/auth/passdb-ldap.c +@@ -376,9 +376,12 @@ ldap_verify_plain(struct auth_request *r + return; + } + ++ const struct settings_get_params params = { ++ .escape_func = ldap_escape, ++ }; + const struct ldap_pre_settings *ldap_pre = NULL; +- if (settings_get(event, &ldap_pre_setting_parser_info, 0, +- &ldap_pre, &error) < 0 || ++ if (settings_get_params(event, &ldap_pre_setting_parser_info, ++ ¶ms, &ldap_pre, &error) < 0 || + ldap_pre_settings_post_check(ldap_pre, DB_LDAP_LOOKUP_TYPE_PASSDB, + &error) < 0) { + e_error(event, "%s", error); +@@ -414,10 +417,13 @@ static void ldap_lookup_credentials(stru + auth_request_ref(request); + ldap_request->request.ldap.auth_request = request; + ++ const struct settings_get_params params = { ++ .escape_func = ldap_escape, ++ }; + const char *error; + const struct ldap_pre_settings *ldap_pre = NULL; +- if (settings_get(event, &ldap_pre_setting_parser_info, 0, +- &ldap_pre, &error) < 0 || ++ if (settings_get_params(event, &ldap_pre_setting_parser_info, ¶ms, ++ &ldap_pre, &error) < 0 || + ldap_pre_settings_post_check(ldap_pre, DB_LDAP_LOOKUP_TYPE_PASSDB, + &error) < 0) { + e_error(event, "%s", error); +@@ -446,8 +452,13 @@ static int passdb_ldap_preinit(pool_t po + if (settings_get(event, &auth_passdb_post_setting_parser_info, + RAW_SETTINGS, &auth_post, error_r) < 0) + goto failed; +- if (settings_get(event, &ldap_pre_setting_parser_info, +- RAW_SETTINGS, &ldap_pre, error_r) < 0) ++ ++ const struct settings_get_params params = { ++ .escape_func = ldap_escape, ++ .flags = RAW_SETTINGS, ++ }; ++ if (settings_get_params(event, &ldap_pre_setting_parser_info, ++ ¶ms, &ldap_pre, error_r) < 0) + goto failed; + + module = p_new(pool, struct ldap_passdb_module, 1); +Index: dovecot/src/auth/userdb-ldap.c +=================================================================== +--- dovecot.orig/src/auth/userdb-ldap.c ++++ dovecot/src/auth/userdb-ldap.c +@@ -122,9 +122,12 @@ static void userdb_ldap_lookup(struct au + struct userdb_ldap_request *request; + const char *error; + ++ const struct settings_get_params params = { ++ .escape_func = ldap_escape, ++ }; + const struct ldap_pre_settings *ldap_pre = NULL; +- if (settings_get(event, &ldap_pre_setting_parser_info, 0, +- &ldap_pre, &error) < 0 || ++ if (settings_get_params(event, &ldap_pre_setting_parser_info, ¶ms, ++ &ldap_pre, &error) < 0 || + ldap_pre_settings_post_check(ldap_pre, DB_LDAP_LOOKUP_TYPE_USERDB, + &error) < 0) { + e_error(event, "%s", error); +@@ -258,9 +261,12 @@ userdb_ldap_iterate_init(struct auth_req + request = &ctx->request; + request->ctx = ctx; + ++ const struct settings_get_params params = { ++ .escape_func = ldap_escape, ++ }; + const struct ldap_pre_settings *ldap_pre = NULL; +- if (settings_get(event, &ldap_pre_setting_parser_info, 0, +- &ldap_pre, &error) < 0 || ++ if (settings_get_params(event, &ldap_pre_setting_parser_info, ¶ms, ++ &ldap_pre, &error) < 0 || + ldap_pre_settings_post_check(ldap_pre, DB_LDAP_LOOKUP_TYPE_ITERATE, + &error) < 0) { + e_error(event, "%s", error); +@@ -331,8 +337,13 @@ static int userdb_ldap_preinit(pool_t po + if (settings_get(event, &ldap_post_setting_parser_info, + RAW_SETTINGS, &ldap_post, error_r) < 0) + goto failed; +- if (settings_get(event, &ldap_pre_setting_parser_info, +- RAW_SETTINGS, &ldap_pre, error_r) < 0) ++ ++ const struct settings_get_params params = { ++ .escape_func = ldap_escape, ++ .flags = RAW_SETTINGS, ++ }; ++ if (settings_get_params(event, &ldap_pre_setting_parser_info, ++ ¶ms, &ldap_pre, error_r) < 0) + goto failed; + + module = p_new(pool, struct ldap_userdb_module, 1); diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-3.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-3.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-3.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,32 @@ +From 926057d36ce5d3ec6833c6a6d3e3b732e0a39453 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Mon, 23 Feb 2026 13:37:09 +0200 +Subject: [PATCH 06/24] lib-settings: settings_get_params() - Fix using + provided escape_func + +This fixes auth-sql and auth-ldap to actually do escaping. +--- + src/lib-settings/settings.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +Index: dovecot/src/lib-settings/settings.c +=================================================================== +--- dovecot.orig/src/lib-settings/settings.c ++++ dovecot/src/lib-settings/settings.c +@@ -1540,8 +1540,14 @@ settings_var_expand_init(struct settings + ctx->var_params.tables_arr = array_front(&init_ctx.tables); + ctx->var_params.providers_arr = array_front(&init_ctx.providers); + ctx->var_params.contexts = array_front(&init_ctx.contexts); +- ctx->var_params.escape_func = init_ctx.escape_func; +- ctx->var_params.escape_context = init_ctx.escape_context; ++ if (ctx->escape_func != NULL) { ++ /* settings_get_params()'s escape_func overrides all others */ ++ ctx->var_params.escape_func = ctx->escape_func; ++ ctx->var_params.escape_context = ctx->escape_context; ++ } else { ++ ctx->var_params.escape_func = init_ctx.escape_func; ++ ctx->var_params.escape_context = init_ctx.escape_context; ++ } + ctx->var_params.event = ctx->event; + } + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-4.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-4.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-4.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-4.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,64 @@ +From cb09189599e28fa8f46790aeb440a35ba8414908 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Mon, 23 Feb 2026 19:33:16 +0200 +Subject: [PATCH 07/24] auth: test-auth - Run Lua unit tests even when building + Lua as plugin + +--- + src/auth/Makefile.am | 7 +++++-- + src/auth/test-lua.c | 2 +- + src/auth/test-main.c | 2 +- + 3 files changed, 7 insertions(+), 4 deletions(-) + +--- dovecot-2.4.1+dfsg1.orig/src/auth/Makefile.am ++++ dovecot-2.4.1+dfsg1/src/auth/Makefile.am +@@ -131,6 +131,8 @@ auth_common_sources = \ + $(ldap_sources) \ + $(lua_sources) + ++test_auth_ldadd_plugins = ++ + headers = \ + auth.h \ + auth-cache.h \ +@@ -185,6 +187,7 @@ libauthdb_lua_la_LDFLAGS = -module -avoi + libauthdb_lua_la_LIBADD = $(LIBDOVECOT_LUA) + libauthdb_lua_la_CPPFLAGS = $(AM_CPPFLAGS) -DPLUGIN_BUILD + libauthdb_lua_la_SOURCES = $(lua_sources) ++test_auth_ldadd_plugins += libauthdb_lua.la + endif + + libauthdb_imap_la_LDFLAGS = -module -avoid-version -shared +@@ -226,8 +229,8 @@ test_auth_SOURCES = \ + test-mock.c \ + test-main.c + +-test_auth_LDADD = $(LIBDOVECOT) $(auth_libs) $(AUTH_LIBS) $(LUA_LIBS) +-test_auth_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(LIBDOVECOT_DEPS) ++test_auth_LDADD = $(test_auth_ldadd_plugins) $(LIBDOVECOT) $(LIBDOVECOT_LUA) $(auth_libs) $(AUTH_LIBS) $(LUA_LIBS) ++test_auth_DEPENDENCIES = $(test_auth_ldadd_plugins) $(pkglibexec_PROGRAMS) $(LIBDOVECOT_DEPS) + + test_mech_SOURCES = \ + $(auth_common_sources) \ +--- dovecot-2.4.1+dfsg1.orig/src/auth/test-lua.c ++++ dovecot-2.4.1+dfsg1/src/auth/test-lua.c +@@ -2,7 +2,7 @@ + + #include "test-auth.h" + +-#ifdef BUILTIN_LUA ++#ifdef HAVE_LUA + #include "istream.h" + #include "auth-settings.h" + #include "auth-request.h" +--- dovecot-2.4.1+dfsg1.orig/src/auth/test-main.c ++++ dovecot-2.4.1+dfsg1/src/auth/test-main.c +@@ -22,7 +22,7 @@ int main(int argc, char *argv[]) + TEST_NAMED(test_auth_request_var_expand) + TEST_NAMED(test_auth_request_fields) + TEST_NAMED(test_username_filter) +-#if defined(BUILTIN_LUA) ++#if defined(HAVE_LUA) + TEST_NAMED(test_db_lua) + #endif + { NULL, NULL } diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-5.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-5.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-5.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-5.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,224 @@ +From 46fbe8a5b82d548fcf5cb352bb2510bb7292174a Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Mon, 23 Feb 2026 19:54:40 +0200 +Subject: [PATCH 08/24] auth: Rewrite ldap_escape() with a unit test + +--- + src/auth/Makefile.am | 2 + + src/auth/db-ldap.c | 89 +++++++++++++++++++++++++++++++++++--------- + src/auth/db-ldap.h | 1 + + src/auth/test-auth.h | 1 + + src/auth/test-ldap.c | 39 +++++++++++++++++++ + src/auth/test-main.c | 3 ++ + 6 files changed, 117 insertions(+), 18 deletions(-) + create mode 100644 src/auth/test-ldap.c + +--- dovecot-2.4.1+dfsg1.orig/src/auth/Makefile.am ++++ dovecot-2.4.1+dfsg1/src/auth/Makefile.am +@@ -178,6 +178,7 @@ libauthdb_ldap_la_LDFLAGS = -module -avo + libauthdb_ldap_la_LIBADD = $(LIBDOVECOT_LDAP) $(LDAP_LIBS) + libauthdb_ldap_la_CPPFLAGS = $(AM_CPPFLAGS) -DPLUGIN_BUILD + libauthdb_ldap_la_SOURCES = $(ldap_sources) ++test_auth_ldadd_plugins += libauthdb_ldap.la + else + auth_libs += $(LIBDOVECOT_LDAP) + endif +@@ -225,6 +226,7 @@ test_auth_SOURCES = \ + test-auth-request-var-expand.c \ + test-auth-request-fields.c \ + test-username-filter.c \ ++ test-ldap.c \ + test-lua.c \ + test-mock.c \ + test-main.c +--- dovecot-2.4.1+dfsg1.orig/src/auth/db-ldap.c ++++ dovecot-2.4.1+dfsg1/src/auth/db-ldap.c +@@ -13,6 +13,7 @@ + #include "str.h" + #include "strescape.h" + #include "time-util.h" ++#include "unichar.h" + #include "env-util.h" + #include "settings.h" + #include "ssl-settings.h" +@@ -54,8 +55,6 @@ + #define DB_LDAP_REQUEST_MAX_ATTEMPT_COUNT 3 + #define DB_LDAP_ATTR_DN "~dn" + +-static const char *LDAP_ESCAPE_CHARS = "*,\\#+<>;\"()= "; +- + struct db_ldap_result { + int refcount; + LDAPMessage *msg; +@@ -1139,26 +1138,80 @@ void db_ldap_get_attribute_names(pool_t + *sensitive_r = array_front(&sensitive_attr_names); + } + +-#define IS_LDAP_ESCAPED_CHAR(c) \ +- ((((unsigned char)(c)) & 0x80) != 0 || strchr(LDAP_ESCAPE_CHARS, (c)) != NULL) +- +-const char *ldap_escape(const char *str, +- void *context ATTR_UNUSED) ++const char *ldap_escape(const char *input, void *context ATTR_UNUSED) + { +- string_t *ret = NULL; +- +- for (const char *p = str; *p != '\0'; p++) { +- if (IS_LDAP_ESCAPED_CHAR(*p)) { +- if (ret == NULL) { +- ret = t_str_new((size_t) (p - str) + 64); +- str_append_data(ret, str, (size_t) (p - str)); +- } +- str_printfa(ret, "\\%02X", (unsigned char)*p); +- } else if (ret != NULL) +- str_append_c(ret, *p); ++ /* This function escapes both LDAP filters and LDAP DNs. This works, ++ because both allow using the method of escaping any characters. ++ However, this isn't really recommended, since apparently some LDAP ++ servers and perhaps other tools don't handle unnecessary escaping ++ correctly. ++ ++ Another issue is what to do with invalid UTF-8. LDAP filter RFC ++ suggests just escaping invalid UTF-8. LDAP DN RFC doesn't at least ++ explicitly say that. It instead seems to imply that the string is ++ just expected to be valid UTF-8. Or perhaps to use #hex-string ++ encoding, which isn't compatible with LDAP filters. Just to be safe, ++ we'll replace invalid UTF-8 input with the UTF-8 replacement ++ character. ++ ++ So this function isn't perhaps the most standards compliant one, but ++ we don't really care, since this escaping is mainly intended to ++ prevent untrusted username input from breaking out of the filter or ++ DN. Valid usernames are unlikely to require escaping or to be ++ invalid UTF-8. */ ++ ++ /* Convert invalid UTF-8 characters to replacement characters. */ ++ size_t input_len = strlen(input); ++ string_t *str = t_str_new(64); ++ if (!uni_utf8_get_valid_data((const unsigned char *)input, ++ input_len, str)) { ++ /* Input was not valid UTF-8 */ ++ input = t_strdup(str_c(str)); ++ input_len = str_len(str); ++ str_truncate(str, 0); ++ } ++ ++ const char *escape_chars = ++ /* control characters */ ++ "\x01\x02\x03\x04\x05\x06\x07\x08" ++ "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" ++ "\x11\x12\x03\x14\x15\x16\x17\x18" ++ "\x19\x1a\x1b\x1c\x1d\x1e\x1f" ++ /* filter + DN */ ++ "\\" ++ /* filter only */ ++ "*()" ++ /* DN only (not including leading and trailing chars) */ ++ ",+<>;\"="; ++ size_t pos; ++ ++ /* check the leading character separately (required by DN) */ ++ if (input[0] == '#' || input[0] == ' ') ++ pos = 0; ++ else { ++ pos = strcspn(input, escape_chars); ++ if (pos == input_len) { ++ /* the last trailing space is escaped ++ (required by DN) */ ++ if (pos > 0 && input[pos - 1] == ' ') ++ pos--; ++ else ++ return input; ++ } + } + +- return ret == NULL ? str : str_c(ret); ++ do { ++ str_append_data(str, input, pos); ++ str_printfa(str, "\\%02x", (unsigned char)input[pos]); ++ input += pos + 1; ++ input_len -= pos + 1; ++ pos = strcspn(input, escape_chars); ++ /* the last trailing space is escaped (required by DN) */ ++ if (pos == input_len && pos > 0 && input[pos - 1] == ' ') ++ pos--; ++ } while (pos < input_len); ++ str_append_data(str, input, pos); ++ return str_c(str); + } + + static bool +--- dovecot-2.4.1+dfsg1.orig/src/auth/db-ldap.h ++++ dovecot-2.4.1+dfsg1/src/auth/db-ldap.h +@@ -169,6 +169,7 @@ void db_ldap_connect_delayed(struct ldap + + void db_ldap_enable_input(struct ldap_connection *conn, bool enable); + ++const char *ldap_dn_escape(const char *str, void *context); + const char *ldap_escape(const char *str, void *context); + const char *ldap_get_error(struct ldap_connection *conn); + +--- dovecot-2.4.1+dfsg1.orig/src/auth/test-auth.h ++++ dovecot-2.4.1+dfsg1/src/auth/test-auth.h +@@ -14,6 +14,7 @@ void test_auth_request_var_expand(void); + void test_auth_request_fields(void); + void test_db_dict_parse_cache_key(void); + void test_username_filter(void); ++void test_db_ldap(void); + void test_db_lua(void); + struct auth_passdb *passdb_mock(void); + void passdb_mock_mod_init(void); +--- /dev/null ++++ dovecot-2.4.1+dfsg1/src/auth/test-ldap.c +@@ -0,0 +1,39 @@ ++/* Copyright (c) 2026 Dovecot authors, see the included COPYING file */ ++ ++#include "test-auth.h" ++ ++#ifdef HAVE_LDAP ++#include "db-ldap.h" ++ ++static void test_ldap_escape(void) ++{ ++ struct { ++ const char *input; ++ const char *output; ++ } tests[] = { ++ { "", "" }, ++ { " ", "\\20" }, ++ { " ", "\\20\\20" }, ++ { "foo ", "foo\\20" }, ++ { ",", "\\2c" }, ++ { "s p a c e", "s p a c e" }, ++ { "# start-end#", "\\23 start-end#" }, ++ { " start-end2 ", "\\20 start-end2 \\20" }, ++ { "middle:,+\"\\<>;=", "middle:\\2c\\2b\\22\\5c\\3c\\3e\\3b\\3d" }, ++ { "valid-utf8:\xc3\xb1", "valid-utf8:\xc3\xb1" }, ++ { "Bad \xFF Characters", "Bad \xEF\xBF\xBD Characters" }, ++ }; ++ test_begin("ldap_escape()"); ++ for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { ++ test_assert_strcmp_idx(ldap_escape(tests[i].input, NULL), ++ tests[i].output, i); ++ } ++ test_end(); ++} ++ ++void test_db_ldap(void) ++{ ++ test_ldap_escape(); ++} ++ ++#endif +--- dovecot-2.4.1+dfsg1.orig/src/auth/test-main.c ++++ dovecot-2.4.1+dfsg1/src/auth/test-main.c +@@ -25,6 +25,9 @@ int main(int argc, char *argv[]) + #if defined(HAVE_LUA) + TEST_NAMED(test_db_lua) + #endif ++#if defined(HAVE_LDAP) ++ TEST_NAMED(test_db_ldap) ++#endif + { NULL, NULL } + }; + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-6.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-6.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-6.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-6.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,36 @@ +From 568fe0b2f57fd7563d053e5ef3127d3a969b0acc Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Tue, 24 Feb 2026 12:24:37 +0200 +Subject: [PATCH 09/24] auth: passdb sql - Fix escaping for set_credentials() + +This was only used by OTP SASL mechanism after successful authentication, so +it practically couldn't be used for SQL injections. + +Broken by ef0c63b690e6ef9fbd53cb815dfab50d1667ba3a +--- + src/auth/passdb-sql.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/src/auth/passdb-sql.c b/src/auth/passdb-sql.c +index f3682d4926..28291606ea 100644 +--- a/src/auth/passdb-sql.c ++++ b/src/auth/passdb-sql.c +@@ -258,8 +258,13 @@ static void sql_set_credentials(struct auth_request *request, + + request->mech_password = p_strdup(request->pool, new_credentials); + +- if (settings_get(authdb_event(request), &passdb_sql_setting_parser_info, 0, +- &set, &error) < 0) { ++ const struct settings_get_params params = { ++ .escape_func = passdb_sql_escape, ++ .escape_context = module->db, ++ }; ++ if (settings_get_params(authdb_event(request), ++ &passdb_sql_setting_parser_info, ¶ms, ++ &set, &error) < 0) { + e_error(authdb_event(request), "%s", error); + callback(FALSE, request); + return; +-- +2.39.5 + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-7.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-7.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-7.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-7.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,37 @@ +From dfce1f1e7214b97ef73fa851655d176455b836fd Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Tue, 24 Feb 2026 12:26:46 +0200 +Subject: [PATCH 10/24] auth: userdb sql - Fix escaping for user iteration + +This is mostly a non-issue, since userdb iteration doesn't take any +untrusted input. + +Broken by ef0c63b690e6ef9fbd53cb815dfab50d1667ba3a +--- + src/auth/userdb-sql.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/src/auth/userdb-sql.c b/src/auth/userdb-sql.c +index 349f61c49e..09bac48650 100644 +--- a/src/auth/userdb-sql.c ++++ b/src/auth/userdb-sql.c +@@ -180,9 +180,13 @@ userdb_sql_iterate_init(struct auth_request *auth_request, + ctx->ctx.context = context; + auth_request_ref(auth_request); + +- if (settings_get(authdb_event(auth_request), +- &userdb_sql_setting_parser_info, 0, +- &set, &error) < 0) { ++ const struct settings_get_params params = { ++ .escape_func = userdb_sql_escape, ++ .escape_context = module->db, ++ }; ++ if (settings_get_params(authdb_event(auth_request), ++ &userdb_sql_setting_parser_info, ¶ms, ++ &set, &error) < 0) { + e_error(authdb_event(auth_request), "%s", error); + ctx->ctx.failed = TRUE; + return &ctx->ctx; +-- +2.39.5 + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-8.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-8.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-8.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-24031-27860-8.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,60 @@ +From c6b15902a0e399bd6bf035f7e8869d201e9394d2 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Wed, 25 Feb 2026 12:40:22 +0200 +Subject: [PATCH 11/24] lib-var-expand: Add "safe" filter to prevent escaping + output + +For example ldap_base = %{passdb:next_dn | safe} to avoid escaping the DN. +--- + src/lib-var-expand/expansion-filter.c | 9 +++++++++ + src/lib-var-expand/expansion-program.c | 3 ++- + src/lib-var-expand/var-expand-private.h | 1 + + 3 files changed, 12 insertions(+), 1 deletion(-) + +--- dovecot-2.4.1+dfsg1.orig/src/lib-var-expand/expansion-filter.c ++++ dovecot-2.4.1+dfsg1/src/lib-var-expand/expansion-filter.c +@@ -1130,6 +1130,14 @@ static int fn_text(const struct var_expa + return 0; + } + ++static int fn_safe(const struct var_expand_statement *stmt ATTR_UNUSED, ++ struct var_expand_state *state, ++ const char **error_r ATTR_UNUSED) ++{ ++ state->transfer_safe = TRUE; ++ return 0; ++} ++ + static const struct var_expand_filter var_expand_builtin_filters[] = { + { .name = "lookup", .filter = fn_lookup }, + { .name = "literal", .filter = fn_literal }, +@@ -1167,6 +1175,7 @@ static const struct var_expand_filter va + { .name = "text", .filter = fn_text }, + { .name = "encrypt", .filter = expansion_filter_encrypt }, + { .name = "decrypt", .filter = expansion_filter_decrypt }, ++ { .name = "safe", .filter = fn_safe }, + { .name = NULL } + }; + +--- dovecot-2.4.1+dfsg1.orig/src/lib-var-expand/expansion-program.c ++++ dovecot-2.4.1+dfsg1/src/lib-var-expand/expansion-program.c +@@ -140,7 +140,8 @@ int var_expand_program_execute(string_t + if (state.transfer_binary) + var_expand_state_set_transfer(&state, binary_to_hex(state.transfer->data, state.transfer->used)); + if (state.transfer_set) { +- if (!program->only_literal && params->escape_func != NULL) { ++ if (!program->only_literal && !state.transfer_safe && ++ params->escape_func != NULL) { + str_append(state.result, + params->escape_func(str_c(state.transfer), + params->escape_context)); +--- dovecot-2.4.1+dfsg1.orig/src/lib-var-expand/var-expand-private.h ++++ dovecot-2.4.1+dfsg1/src/lib-var-expand/var-expand-private.h +@@ -73,6 +73,7 @@ struct var_expand_state { + string_t *transfer; + bool transfer_set:1; + bool transfer_binary:1; ++ bool transfer_safe:1; + }; + + struct var_expand_statement { diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-1.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-1.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-1.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,21 @@ +From f1bc3ea8ba747fcfe14ab56685d400e4e3cff130 Mon Sep 17 00:00:00 2001 +From: Aki Tuomi +Date: Mon, 9 Mar 2026 20:04:27 +0200 +Subject: [PATCH 21/24] auth: cache - Use translated username in + auth_cache_remove() + +--- + src/auth/auth-cache.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- dovecot-2.4.1+dfsg1.orig/src/auth/auth-cache.c ++++ dovecot-2.4.1+dfsg1/src/auth/auth-cache.c +@@ -474,7 +474,7 @@ void auth_cache_remove(struct auth_cache + { + struct auth_cache_node *node; + +- key = auth_request_expand_cache_key(request, key, request->fields.user); ++ key = auth_request_expand_cache_key(request, key, request->fields.translated_username); + node = hash_table_lookup(cache->hash, key); + if (node == NULL) + return; diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-2.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-2.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-2.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,92 @@ +From f2119a25a439f599b0a30fc674300b8f354e5b67 Mon Sep 17 00:00:00 2001 +From: Aki Tuomi +Date: Wed, 11 Mar 2026 12:30:32 +0200 +Subject: [PATCH 22/24] auth: Move passdb event lifecycle handling to + auth_request_passdb_event_(begin|end) + +--- + src/auth/auth-request.c | 39 +++++++++++++++++++++++++++------------ + src/auth/auth-request.h | 3 +++ + 2 files changed, 30 insertions(+), 12 deletions(-) + +--- dovecot-2.4.1+dfsg1.orig/src/auth/auth-request.c ++++ dovecot-2.4.1+dfsg1/src/auth/auth-request.c +@@ -663,15 +663,10 @@ auth_request_cache_result_to_str(enum au + } + } + +-void auth_request_passdb_lookup_begin(struct auth_request *request) ++void auth_request_passdb_event_begin(struct auth_request *request) + { + struct event *event; + +- i_assert(request->passdb != NULL); +- i_assert(!request->userdb_lookup); +- +- request->passdb_cache_result = AUTH_REQUEST_CACHE_NONE; +- + /* use passdb-specific settings during the passdb lookup */ + request->set = request->passdb->auth_set; + +@@ -691,11 +686,35 @@ void auth_request_passdb_lookup_begin(st + event_set_min_log_level(event, request->passdb->auth_set->verbose ? + LOG_TYPE_INFO : LOG_TYPE_WARNING); + ++ array_push_back(&request->authdb_event, &event); ++} ++ ++void auth_request_passdb_event_end(struct auth_request *request) ++{ ++ struct event *event = authdb_event(request); ++ event_unref(&event); ++ array_pop_back(&request->authdb_event); ++ ++ /* restore protocol-specific settings */ ++ request->set = request->protocol_set; ++} ++ ++void auth_request_passdb_lookup_begin(struct auth_request *request) ++{ ++ struct event *event; ++ ++ i_assert(request->passdb != NULL); ++ i_assert(!request->userdb_lookup); ++ auth_request_passdb_event_begin(request); ++ ++ event = authdb_event(request); ++ ++ request->passdb_cache_result = AUTH_REQUEST_CACHE_NONE; ++ + e_debug(event_create_passthrough(event)-> + set_name("auth_passdb_request_started")-> + event(), + "Performing passdb lookup"); +- array_push_back(&request->authdb_event, &event); + } + + void auth_request_passdb_lookup_end(struct auth_request *request, +@@ -713,11 +732,7 @@ void auth_request_passdb_lookup_end(stru + request->passdb_cache_result)); + } + e_debug(e->event(), "Finished passdb lookup"); +- event_unref(&event); +- array_pop_back(&request->authdb_event); +- +- /* restore protocol-specific settings */ +- request->set = request->protocol_set; ++ auth_request_passdb_event_end(request); + } + + void auth_request_userdb_lookup_begin(struct auth_request *request) +--- dovecot-2.4.1+dfsg1.orig/src/auth/auth-request.h ++++ dovecot-2.4.1+dfsg1/src/auth/auth-request.h +@@ -427,6 +427,9 @@ void auth_request_log_finished(struct au + void auth_request_master_user_login_finish(struct auth_request *request); + void auth_request_fields_init(struct auth_request *request); + ++void auth_request_passdb_event_begin(struct auth_request *request); ++void auth_request_passdb_event_end(struct auth_request *request); ++ + void auth_request_passdb_lookup_begin(struct auth_request *request); + void auth_request_passdb_lookup_end(struct auth_request *request, + enum passdb_result result); diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-3.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-3.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-3.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,44 @@ +From 8fd279db61be2054c8f3e7275766717318a2df91 Mon Sep 17 00:00:00 2001 +From: Aki Tuomi +Date: Mon, 9 Mar 2026 21:23:29 +0200 +Subject: [PATCH 23/24] auth: Initialize set_credentials event properly + +Fixes update_query +--- + src/auth/auth-request.c | 4 ++++ + src/auth/auth-worker-server.c | 2 ++ + 2 files changed, 6 insertions(+) + +--- dovecot-2.4.1+dfsg1.orig/src/auth/auth-request.c ++++ dovecot-2.4.1+dfsg1/src/auth/auth-request.c +@@ -1528,6 +1528,8 @@ void auth_request_set_credentials(struct + struct auth_passdb *passdb = request->passdb; + const char *cache_key, *new_credentials; + ++ auth_request_passdb_event_begin(request); ++ + cache_key = passdb_cache == NULL ? NULL : passdb->cache_key; + if (cache_key != NULL) + auth_cache_remove(passdb_cache, request, cache_key); +@@ -1544,6 +1546,8 @@ void auth_request_set_credentials(struct + /* this passdb doesn't support credentials update */ + callback(FALSE, request); + } ++ ++ auth_request_passdb_event_end(request); + } + + static void +--- dovecot-2.4.1+dfsg1.orig/src/auth/auth-worker-server.c ++++ dovecot-2.4.1+dfsg1/src/auth/auth-worker-server.c +@@ -505,8 +505,10 @@ auth_worker_handle_setcred(struct auth_w + } + } + ++ auth_request_passdb_event_begin(auth_request); + auth_request->passdb->passdb->iface. + set_credentials(auth_request, creds, set_credentials_callback); ++ auth_request_passdb_event_end(auth_request); + return TRUE; + } + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-4.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-4.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-4.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27855-4.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,30 @@ +From 535a14209810e7b0c8f94479daf580ac4c637904 Mon Sep 17 00:00:00 2001 +From: Aki Tuomi +Date: Wed, 11 Mar 2026 12:46:53 +0200 +Subject: [PATCH 24/24] auth: passdb-sql - Require update_query to be set when + used + +--- + src/auth/passdb-sql.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/auth/passdb-sql.c b/src/auth/passdb-sql.c +index 28291606ea..ec160e3d75 100644 +--- a/src/auth/passdb-sql.c ++++ b/src/auth/passdb-sql.c +@@ -270,6 +270,12 @@ static void sql_set_credentials(struct auth_request *request, + return; + } + ++ if (*set->update_query == '\0') { ++ e_error(authdb_event(request), "passdb_sql_update_query is empty"); ++ callback(FALSE, request); ++ return; ++ } ++ + sql_request = i_new(struct passdb_sql_request, 1); + sql_request->auth_request = request; + sql_request->callback.set_credentials = callback; +-- +2.39.5 + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27856-1.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27856-1.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27856-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27856-1.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,54 @@ +From 6ef282fab8af8faec75b0ff8cb87b11094642ba0 Mon Sep 17 00:00:00 2001 +From: Aki Tuomi +Date: Wed, 4 Mar 2026 08:05:13 +0200 +Subject: [PATCH 16/24] doveadm: client-connection - Use timing safe credential + check + +--- + src/doveadm/client-connection-http.c | 7 +++++-- + src/doveadm/client-connection-tcp.c | 4 +--- + 2 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/src/doveadm/client-connection-http.c b/src/doveadm/client-connection-http.c +index 81ae9f51da..f760548653 100644 +--- a/src/doveadm/client-connection-http.c ++++ b/src/doveadm/client-connection-http.c +@@ -973,7 +973,9 @@ doveadm_http_server_auth_basic(struct client_request_http *req, + value = p_strdup_printf(conn->conn.pool, + "doveadm:%s", set->doveadm_password); + base64_encode(value, strlen(value), b64_value); +- if (creds->data != NULL && strcmp(creds->data, str_c(b64_value)) == 0) ++ ++ if (creds->data != NULL && ++ str_equals_timing_almost_safe(value, creds->data)) + return TRUE; + + e_error(conn->conn.event, +@@ -1000,7 +1002,8 @@ doveadm_http_server_auth_api_key(struct client_request_http *req, + b64_value = str_new(conn->conn.pool, 32); + base64_encode(set->doveadm_api_key, + strlen(set->doveadm_api_key), b64_value); +- if (creds->data != NULL && strcmp(creds->data, str_c(b64_value)) == 0) ++ if (creds->data != NULL && ++ str_equals_timing_almost_safe(creds->data, str_c(b64_value))) + return TRUE; + + e_error(conn->conn.event, +diff --git a/src/doveadm/client-connection-tcp.c b/src/doveadm/client-connection-tcp.c +index 936bb03f01..cb16f7214b 100644 +--- a/src/doveadm/client-connection-tcp.c ++++ b/src/doveadm/client-connection-tcp.c +@@ -400,9 +400,7 @@ client_connection_tcp_authenticate(struct client_connection_tcp *conn) + return -1; + } + pass = t_strndup(data + 9, size - 9); +- if (strlen(pass) != strlen(set->doveadm_password) || +- !mem_equals_timing_safe(pass, set->doveadm_password, +- strlen(pass))) { ++ if (!str_equals_timing_almost_safe(pass, set->doveadm_password)) { + e_error(conn->conn.event, + "doveadm client authenticated with wrong password"); + return -1; +-- +2.39.5 + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27856-2.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27856-2.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27856-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27856-2.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,54 @@ +From 6d8c50154744284304ceeab69c3951e6d6852007 Mon Sep 17 00:00:00 2001 +From: Aki Tuomi +Date: Wed, 4 Mar 2026 09:28:18 +0200 +Subject: [PATCH 17/24] doveadm: Use datastack for temporary b64 value + +There is no need to allocate it from connection pool. +--- + src/doveadm/client-connection-http.c | 14 +++++--------- + 1 file changed, 5 insertions(+), 9 deletions(-) + +diff --git a/src/doveadm/client-connection-http.c b/src/doveadm/client-connection-http.c +index f760548653..5d927d1979 100644 +--- a/src/doveadm/client-connection-http.c ++++ b/src/doveadm/client-connection-http.c +@@ -960,7 +960,7 @@ doveadm_http_server_auth_basic(struct client_request_http *req, + struct client_connection_http *conn = req->conn; + const struct doveadm_settings *set = conn->conn.set; + string_t *b64_value; +- char *value; ++ const char *value; + + if (*set->doveadm_password == '\0') { + e_error(conn->conn.event, +@@ -969,13 +969,11 @@ doveadm_http_server_auth_basic(struct client_request_http *req, + return FALSE; + } + +- b64_value = str_new(conn->conn.pool, 32); +- value = p_strdup_printf(conn->conn.pool, +- "doveadm:%s", set->doveadm_password); +- base64_encode(value, strlen(value), b64_value); ++ value = t_strdup_printf("doveadm:%s", set->doveadm_password); ++ b64_value = t_base64_encode_str(0, UINT_MAX, value); + + if (creds->data != NULL && +- str_equals_timing_almost_safe(value, creds->data)) ++ str_equals_timing_almost_safe(str_c(b64_value), creds->data)) + return TRUE; + + e_error(conn->conn.event, +@@ -999,9 +997,7 @@ doveadm_http_server_auth_api_key(struct client_request_http *req, + return FALSE; + } + +- b64_value = str_new(conn->conn.pool, 32); +- base64_encode(set->doveadm_api_key, +- strlen(set->doveadm_api_key), b64_value); ++ b64_value = t_base64_encode_str(0, UINT_MAX, set->doveadm_api_key); + if (creds->data != NULL && + str_equals_timing_almost_safe(creds->data, str_c(b64_value))) + return TRUE; +-- +2.39.5 + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27856-3.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27856-3.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27856-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27856-3.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,26 @@ +From cc628976210de17bd26b4d9761ea879ce78b7980 Mon Sep 17 00:00:00 2001 +From: Aki Tuomi +Date: Wed, 4 Mar 2026 14:39:43 +0200 +Subject: [PATCH 18/24] doveadm: client-connection - Get API key from + per-connection settings + +--- + src/doveadm/client-connection-http.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/doveadm/client-connection-http.c b/src/doveadm/client-connection-http.c +index 5d927d1979..dfbc3528d7 100644 +--- a/src/doveadm/client-connection-http.c ++++ b/src/doveadm/client-connection-http.c +@@ -987,7 +987,7 @@ doveadm_http_server_auth_api_key(struct client_request_http *req, + const struct http_auth_credentials *creds) + { + struct client_connection_http *conn = req->conn; +- const struct doveadm_settings *set = doveadm_settings; ++ const struct doveadm_settings *set = conn->conn.set; + string_t *b64_value; + + if (*set->doveadm_api_key == '\0') { +-- +2.39.5 + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-1.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-1.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-1.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,27 @@ +From bc91df5f91e8b83c58f138799d39be687271106d Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +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/pigeonhole/src/plugins/imap-filter-sieve/cmd-filter-sieve.c ++++ b/pigeonhole/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 -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-2.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-2.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-2.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,228 @@ +From ad8e6542518acee4e1a803001b232c03732cdaff Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Fri, 6 Mar 2026 15:25:14 +0200 +Subject: [PATCH 12/24] lib-imap, global: Add params parameter to + imap_parser_create() + +--- + src/imap-login/imap-login-client.c | 4 ++-- + src/imap-login/imap-login-cmd-id.c | 4 +++- + src/imap/cmd-append.c | 3 ++- + src/imap/cmd-setmetadata.c | 3 ++- + src/imap/imap-client.c | 3 ++- + src/lib-imap-client/imapc-connection.c | 3 ++- + src/lib-imap-storage/imap-msgpart.c | 2 +- + src/lib-imap/imap-bodystructure.c | 4 ++-- + src/lib-imap/imap-envelope.c | 2 +- + src/lib-imap/imap-parser.c | 3 ++- + src/lib-imap/imap-parser.h | 6 +++++- + src/lib-imap/test-imap-parser.c | 10 +++++----- + src/plugins/virtual/virtual-config.c | 2 +- + 13 files changed, 30 insertions(+), 19 deletions(-) + +--- dovecot-2.4.1+dfsg1.orig/src/imap-login/imap-login-client.c ++++ dovecot-2.4.1+dfsg1/src/imap-login/imap-login-client.c +@@ -382,7 +382,7 @@ static int imap_client_create(struct cli + imap_client->parser = + imap_parser_create(imap_client->common.input, + imap_client->common.output, +- IMAP_LOGIN_MAX_LINE_LENGTH); ++ IMAP_LOGIN_MAX_LINE_LENGTH, NULL); + struct settings_instance *set_instance = settings_instance_find(client->event); + if (set_instance == NULL) { + set_instance = settings_instance_new( +@@ -468,7 +468,7 @@ static void imap_client_starttls(struct + imap_client->parser = + imap_parser_create(imap_client->common.input, + imap_client->common.output, +- IMAP_LOGIN_MAX_LINE_LENGTH); ++ IMAP_LOGIN_MAX_LINE_LENGTH, NULL); + + /* CRLF is lost from buffer when streams are reopened. */ + imap_client->skip_line = FALSE; +--- dovecot-2.4.1+dfsg1.orig/src/imap-login/imap-login-cmd-id.c ++++ dovecot-2.4.1+dfsg1/src/imap-login/imap-login-cmd-id.c +@@ -346,7 +346,9 @@ int cmd_id(struct imap_client *client) + id->params->pool = param_pool; + id->parser = imap_parser_create(client->common.input, + client->common.output, +- IMAP_LOGIN_MAX_LINE_LENGTH); ++ IMAP_LOGIN_MAX_LINE_LENGTH, ++ NULL); ++ id->log_reply = str_new(default_pool, 64); + if (client->set->imap_literal_minus) + imap_parser_enable_literal_minus(id->parser); + parser_flags = IMAP_PARSE_FLAG_STOP_AT_LIST; +--- dovecot-2.4.1+dfsg1.orig/src/imap/cmd-append.c ++++ dovecot-2.4.1+dfsg1/src/imap/cmd-append.c +@@ -1063,7 +1063,8 @@ static bool cmd_append_full(struct clien + o_stream_unset_flush_callback(client->output); + + ctx->save_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->save_parser); + +--- dovecot-2.4.1+dfsg1.orig/src/imap/cmd-setmetadata.c ++++ dovecot-2.4.1+dfsg1/src/imap/cmd-setmetadata.c +@@ -289,7 +289,8 @@ cmd_setmetadata_start(struct imap_setmet + 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); +--- dovecot-2.4.1+dfsg1.orig/src/imap/imap-client.c ++++ dovecot-2.4.1+dfsg1/src/imap/imap-client.c +@@ -975,7 +975,8 @@ client_command_new(struct client *client + } else { + cmd->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(cmd->parser); + } +--- dovecot-2.4.1+dfsg1.orig/src/lib-imap-client/imapc-connection.c ++++ dovecot-2.4.1+dfsg1/src/lib-imap-client/imapc-connection.c +@@ -1876,7 +1876,8 @@ static void imapc_connection_connect_nex + o_stream_set_flush_callback(conn->output, imapc_connection_connected, + conn); + conn->parser = imap_parser_create(conn->input, NULL, +- conn->client->set->imapc_max_line_length); ++ conn->client->set->imapc_max_line_length, ++ NULL); + conn->to = timeout_add(conn->client->set->imapc_connection_timeout_interval_msecs, + imapc_connection_timeout, conn); + conn->to_output = timeout_add(conn->client->set->imapc_max_idle_time_secs*1000, +--- dovecot-2.4.1+dfsg1.orig/src/lib-imap-storage/imap-msgpart.c ++++ dovecot-2.4.1+dfsg1/src/lib-imap-storage/imap-msgpart.c +@@ -149,7 +149,7 @@ imap_msgpart_get_header_fields(pool_t po + int result = 0; + + input = i_stream_create_from_data(header_list, strlen(header_list)); +- parser = imap_parser_create(input, NULL, SIZE_MAX); ++ parser = imap_parser_create(input, NULL, SIZE_MAX, NULL); + + if (imap_parser_finish_line(parser, 0, 0, &args) > 0 && + imap_arg_get_list_full(args, &hdr_list, &list_count) && +--- dovecot-2.4.1+dfsg1.orig/src/lib-imap/imap-bodystructure.c ++++ dovecot-2.4.1+dfsg1/src/lib-imap/imap-bodystructure.c +@@ -722,7 +722,7 @@ int imap_bodystructure_parse_full(const + input = i_stream_create_from_data(bodystructure, strlen(bodystructure)); + (void)i_stream_read(input); + +- parser = imap_parser_create(input, NULL, SIZE_MAX); ++ parser = imap_parser_create(input, NULL, SIZE_MAX, NULL); + ret = imap_parser_finish_line(parser, 0, + IMAP_PARSE_FLAG_LITERAL_TYPE, &args); + if (ret < 0) { +@@ -972,7 +972,7 @@ int imap_body_parse_from_bodystructure(c + input = i_stream_create_from_data(bodystructure, strlen(bodystructure)); + (void)i_stream_read(input); + +- parser = imap_parser_create(input, NULL, SIZE_MAX); ++ parser = imap_parser_create(input, NULL, SIZE_MAX, NULL); + ret = imap_parser_finish_line(parser, 0, IMAP_PARSE_FLAG_NO_UNESCAPE | + IMAP_PARSE_FLAG_LITERAL_TYPE, &args); + if (ret < 0) { +--- dovecot-2.4.1+dfsg1.orig/src/lib-imap/imap-envelope.c ++++ dovecot-2.4.1+dfsg1/src/lib-imap/imap-envelope.c +@@ -222,7 +222,7 @@ bool imap_envelope_parse(const char *env + input = i_stream_create_from_data(envelope, strlen(envelope)); + (void)i_stream_read(input); + +- parser = imap_parser_create(input, NULL, SIZE_MAX); ++ parser = imap_parser_create(input, NULL, SIZE_MAX, NULL); + ret = imap_parser_finish_line(parser, 0, + IMAP_PARSE_FLAG_LITERAL_TYPE, &args); + if (ret < 0) { +--- dovecot-2.4.1+dfsg1.orig/src/lib-imap/imap-parser.c ++++ dovecot-2.4.1+dfsg1/src/lib-imap/imap-parser.c +@@ -69,7 +69,8 @@ struct imap_parser { + + struct imap_parser * + imap_parser_create(struct istream *input, struct ostream *output, +- size_t max_line_size) ++ size_t max_line_size, ++ const struct imap_parser_params *params ATTR_UNUSED) + { + struct imap_parser *parser; + +--- dovecot-2.4.1+dfsg1.orig/src/lib-imap/imap-parser.h ++++ dovecot-2.4.1+dfsg1/src/lib-imap/imap-parser.h +@@ -38,6 +38,9 @@ enum imap_parser_error { + IMAP_PARSE_ERROR_LITERAL_TOO_BIG + }; + ++struct imap_parser_params { ++}; ++ + struct imap_parser; + + /* Create new IMAP argument parser. output is used for sending command +@@ -53,7 +56,8 @@ struct imap_parser; + 2 * max_line_size. */ + struct imap_parser * + imap_parser_create(struct istream *input, struct ostream *output, +- size_t max_line_size) ATTR_NULL(2); ++ size_t max_line_size, ++ const struct imap_parser_params *params); + void imap_parser_ref(struct imap_parser *parser); + void imap_parser_unref(struct imap_parser **parser); + +--- dovecot-2.4.1+dfsg1.orig/src/lib-imap/test-imap-parser.c ++++ dovecot-2.4.1+dfsg1/src/lib-imap/test-imap-parser.c +@@ -16,7 +16,7 @@ static void test_imap_parser_crlf(void) + + test_begin("imap parser crlf handling"); + input = test_istream_create(test_input); +- parser = imap_parser_create(input, NULL, 1024); ++ parser = imap_parser_create(input, NULL, 1024, NULL); + + /* must return -2 until LF is read */ + for (i = 0; test_input[i] != '\n'; i++) { +@@ -60,7 +60,7 @@ static void test_imap_parser_partial_lis + + test_begin("imap parser partial list"); + input = test_istream_create(test_input); +- parser = imap_parser_create(input, NULL, 1024); ++ parser = imap_parser_create(input, NULL, 1024, NULL); + + (void)i_stream_read(input); + test_assert(imap_parser_read_args(parser, 0, +@@ -128,7 +128,7 @@ static void test_imap_parser_read_tag_cm + if (tests[i].type != COMMAND) { + input = test_istream_create(tests[i].input); + test_assert(i_stream_read(input) > 0); +- parser = imap_parser_create(input, NULL, 1024); ++ parser = imap_parser_create(input, NULL, 1024, NULL); + ret = imap_parser_read_tag(parser, &atom); + test_assert_idx(ret == tests[i].ret, i); + test_assert_idx(ret <= 0 || strcmp(tests[i].tag, atom) == 0, i); +@@ -139,7 +139,7 @@ static void test_imap_parser_read_tag_cm + if (tests[i].type != TAG) { + input = test_istream_create(tests[i].input); + test_assert(i_stream_read(input) > 0); +- parser = imap_parser_create(input, NULL, 1024); ++ parser = imap_parser_create(input, NULL, 1024, NULL); + ret = imap_parser_read_command_name(parser, &atom); + test_assert_idx(ret == tests[i].ret, i); + test_assert_idx(ret <= 0 || strcmp(tests[i].tag, atom) == 0, i); +--- dovecot-2.4.1+dfsg1.orig/src/plugins/virtual/virtual-config.c ++++ dovecot-2.4.1+dfsg1/src/plugins/virtual/virtual-config.c +@@ -54,7 +54,7 @@ virtual_search_args_parse(const string_t + input = i_stream_create_from_data(str_data(rule), str_len(rule)); + (void)i_stream_read(input); + +- imap_parser = imap_parser_create(input, NULL, SIZE_MAX); ++ imap_parser = imap_parser_create(input, NULL, SIZE_MAX, NULL); + ret = imap_parser_finish_line(imap_parser, 0, 0, &args); + if (ret < 0) { + sargs = NULL; diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-3.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-3.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-3.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,154 @@ +From bb845a61f81be978f641b79058d4bae06f27ee39 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Fri, 6 Mar 2026 15:32:29 +0200 +Subject: [PATCH 13/24] lib-imap: Add imap_parser_params.list_count_limit + +--- + src/lib-imap/imap-parser.c | 15 ++++++++++- + src/lib-imap/imap-parser.h | 6 +++++ + src/lib-imap/test-imap-parser.c | 46 +++++++++++++++++++++++++++++++++ + 3 files changed, 66 insertions(+), 1 deletion(-) + +--- dovecot-2.4.1+dfsg1.orig/src/lib-imap/imap-parser.c ++++ dovecot-2.4.1+dfsg1/src/lib-imap/imap-parser.c +@@ -39,6 +39,7 @@ struct imap_parser { + struct istream *input; + struct ostream *output; + size_t max_line_size; ++ unsigned int list_count_limit; + enum imap_parser_flags flags; + + /* reset by imap_parser_reset(): */ +@@ -46,6 +47,7 @@ struct imap_parser { + ARRAY_TYPE(imap_arg_list) root_list; + ARRAY_TYPE(imap_arg_list) *cur_list; + struct imap_arg *list_arg; ++ unsigned int list_count; + + enum arg_parse_type cur_type; + size_t cur_pos; /* parser position in input buffer */ +@@ -70,7 +72,7 @@ struct imap_parser { + struct imap_parser * + imap_parser_create(struct istream *input, struct ostream *output, + size_t max_line_size, +- const struct imap_parser_params *params ATTR_UNUSED) ++ const struct imap_parser_params *params) + { + struct imap_parser *parser; + +@@ -81,6 +83,10 @@ imap_parser_create(struct istream *input + parser->input = input; + parser->output = output; + parser->max_line_size = max_line_size; ++ if (params != NULL && params->list_count_limit > 0) ++ parser->list_count_limit = params->list_count_limit; ++ else ++ parser->list_count_limit = UINT_MAX; + + p_array_init(&parser->root_list, parser->pool, LIST_INIT_COUNT); + parser->cur_list = &parser->root_list; +@@ -122,6 +128,7 @@ void imap_parser_reset(struct imap_parse + p_array_init(&parser->root_list, parser->pool, LIST_INIT_COUNT); + parser->cur_list = &parser->root_list; + parser->list_arg = NULL; ++ parser->list_count = 0; + + parser->cur_type = ARG_PARSE_NONE; + parser->cur_pos = 0; +@@ -210,6 +217,12 @@ static bool imap_parser_close_list(struc + parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX; + return FALSE; + } ++ if (parser->list_count >= parser->list_count_limit) { ++ parser->error_msg = "Too many '('"; ++ parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX; ++ return FALSE; ++ } ++ parser->list_count++; + + arg = imap_arg_create(parser); + arg->type = IMAP_ARG_EOL; +--- dovecot-2.4.1+dfsg1.orig/src/lib-imap/imap-parser.h ++++ dovecot-2.4.1+dfsg1/src/lib-imap/imap-parser.h +@@ -39,6 +39,12 @@ enum imap_parser_error { + }; + + struct imap_parser_params { ++ /* How many open lists ('(' chars) to allow before faililng the parsing. ++ 0 means unlimited. This is mainly used to prevent excessive memory ++ usage in imap-login process. In imap process there are many other ++ ways to increase memory usage, so we let the max_line_size be the ++ only limit. */ ++ unsigned int list_count_limit; + }; + + struct imap_parser; +--- dovecot-2.4.1+dfsg1.orig/src/lib-imap/test-imap-parser.c ++++ dovecot-2.4.1+dfsg1/src/lib-imap/test-imap-parser.c +@@ -2,6 +2,7 @@ + + #include "lib.h" + #include "istream.h" ++#include "istream-chain.h" + #include "imap-parser.h" + #include "test-common.h" + +@@ -79,6 +80,50 @@ static void test_imap_parser_partial_lis + test_end(); + } + ++static void test_imap_parser_list_limit(void) ++{ ++ struct { ++ const char *input; ++ int ret; ++ } tests[] = { ++ { "(())\r\n", 1 }, ++ { "((()))\r\n", -1 }, ++ }; ++ struct istream_chain *chain; ++ struct istream *chain_input; ++ struct imap_parser *parser; ++ const struct imap_arg *args; ++ ++ test_begin("imap parser list limit"); ++ struct imap_parser_params params = { ++ .list_count_limit = 2, ++ }; ++ ++ for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { ++ chain_input = i_stream_create_chain(&chain, SIZE_MAX); ++ parser = imap_parser_create(chain_input, NULL, 1024, ¶ms); ++ ++ for (unsigned int j = 0; j < 2; j++) { ++ struct istream *input = ++ test_istream_create(tests[i].input); ++ i_stream_chain_append(chain, input); ++ i_stream_unref(&input); ++ ++ (void)i_stream_read(chain_input); ++ ++ test_assert_cmp(imap_parser_read_args(parser, 0, 0, &args), ==, tests[i].ret); ++ /* skip over CRLF */ ++ i_stream_skip(chain_input, i_stream_get_data_size(chain_input)); ++ ++ /* make sure parser reset works */ ++ imap_parser_reset(parser); ++ } ++ imap_parser_unref(&parser); ++ i_stream_destroy(&chain_input); ++ } ++ test_end(); ++} ++ + static void test_imap_parser_read_tag_cmd(void) + { + enum read_type { +@@ -155,6 +200,7 @@ int main(void) + static void (*const test_functions[])(void) = { + test_imap_parser_crlf, + test_imap_parser_partial_list, ++ test_imap_parser_list_limit, + test_imap_parser_read_tag_cmd, + NULL + }; diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-4.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-4.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-4.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-4.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,77 @@ +From ab084588f615b7f2bb6e18c8ee9176511b647aca Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Fri, 6 Mar 2026 15:35:12 +0200 +Subject: [PATCH 14/24] imap-login: Limit the number of open IMAP parser lists + +This prevents attackers from using a large number of '(' in a command to +grow memory usage excessively. +--- + src/imap-login/imap-login-client.c | 10 ++++++++-- + src/imap-login/imap-login-client.h | 4 ++++ + src/imap-login/imap-login-cmd-id.c | 6 +++++- + 3 files changed, 17 insertions(+), 3 deletions(-) + +--- dovecot-2.4.1+dfsg1.orig/src/imap-login/imap-login-client.c ++++ dovecot-2.4.1+dfsg1/src/imap-login/imap-login-client.c +@@ -379,10 +379,13 @@ static int imap_client_create(struct cli + return -1; + } + ++ struct imap_parser_params params = { ++ .list_count_limit = IMAP_LOGIN_LIST_COUNT_LIMIT, ++ }; + imap_client->parser = + imap_parser_create(imap_client->common.input, + imap_client->common.output, +- IMAP_LOGIN_MAX_LINE_LENGTH, NULL); ++ IMAP_LOGIN_MAX_LINE_LENGTH, ¶ms); + struct settings_instance *set_instance = settings_instance_find(client->event); + if (set_instance == NULL) { + set_instance = settings_instance_new( +@@ -464,11 +467,14 @@ static void imap_client_starttls(struct + struct imap_client *imap_client = + container_of(client, struct imap_client, common); + ++ struct imap_parser_params params = { ++ .list_count_limit = IMAP_LOGIN_LIST_COUNT_LIMIT, ++ }; + imap_parser_unref(&imap_client->parser); + imap_client->parser = + imap_parser_create(imap_client->common.input, + imap_client->common.output, +- IMAP_LOGIN_MAX_LINE_LENGTH, NULL); ++ IMAP_LOGIN_MAX_LINE_LENGTH, ¶ms); + + /* CRLF is lost from buffer when streams are reopened. */ + imap_client->skip_line = FALSE; +--- dovecot-2.4.1+dfsg1.orig/src/imap-login/imap-login-client.h ++++ dovecot-2.4.1+dfsg1/src/imap-login/imap-login-client.h +@@ -11,6 +11,10 @@ + /* maximum length for IMAP command line. */ + #define IMAP_LOGIN_MAX_LINE_LENGTH 8192 + ++/* Maximum number of '(' allowed in an IMAP command. Pre-login only uses ++ lists in the ID command. */ ++#define IMAP_LOGIN_LIST_COUNT_LIMIT 1 ++ + enum imap_client_id_state { + IMAP_CLIENT_ID_STATE_LIST = 0, + IMAP_CLIENT_ID_STATE_KEY, +--- dovecot-2.4.1+dfsg1.orig/src/imap-login/imap-login-cmd-id.c ++++ dovecot-2.4.1+dfsg1/src/imap-login/imap-login-cmd-id.c +@@ -344,10 +344,14 @@ int cmd_id(struct imap_client *client) + client->cmd_id = id = i_new(struct imap_client_cmd_id, 1); + id->params = p_new(param_pool, struct imap_id_params, 1); + id->params->pool = param_pool; ++ ++ struct imap_parser_params params = { ++ .list_count_limit = IMAP_LOGIN_LIST_COUNT_LIMIT, ++ }; + id->parser = imap_parser_create(client->common.input, + client->common.output, + IMAP_LOGIN_MAX_LINE_LENGTH, +- NULL); ++ ¶ms); + id->log_reply = str_new(default_pool, 64); + if (client->set->imap_literal_minus) + imap_parser_enable_literal_minus(id->parser); diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-5.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-5.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-5.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27857-5.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,53 @@ +From e0b953af76d754c2984c2867b71fccfef5231bf5 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Mon, 2 Mar 2026 13:50:24 +0200 +Subject: [PATCH 15/24] global: Use const for struct imap_parser_params params + +--- + src/imap-login/imap-login-client.c | 4 ++-- + src/imap-login/imap-login-cmd-id.c | 2 +- + src/lib-imap/test-imap-parser.c | 2 +- + 3 files changed, 4 insertions(+), 4 deletions(-) + +--- dovecot-2.4.1+dfsg1.orig/src/imap-login/imap-login-client.c ++++ dovecot-2.4.1+dfsg1/src/imap-login/imap-login-client.c +@@ -379,7 +379,7 @@ static int imap_client_create(struct cli + return -1; + } + +- struct imap_parser_params params = { ++ const struct imap_parser_params params = { + .list_count_limit = IMAP_LOGIN_LIST_COUNT_LIMIT, + }; + imap_client->parser = +@@ -467,7 +467,7 @@ static void imap_client_starttls(struct + struct imap_client *imap_client = + container_of(client, struct imap_client, common); + +- struct imap_parser_params params = { ++ const struct imap_parser_params params = { + .list_count_limit = IMAP_LOGIN_LIST_COUNT_LIMIT, + }; + imap_parser_unref(&imap_client->parser); +--- dovecot-2.4.1+dfsg1.orig/src/imap-login/imap-login-cmd-id.c ++++ dovecot-2.4.1+dfsg1/src/imap-login/imap-login-cmd-id.c +@@ -345,7 +345,7 @@ int cmd_id(struct imap_client *client) + id->params = p_new(param_pool, struct imap_id_params, 1); + id->params->pool = param_pool; + +- struct imap_parser_params params = { ++ const struct imap_parser_params params = { + .list_count_limit = IMAP_LOGIN_LIST_COUNT_LIMIT, + }; + id->parser = imap_parser_create(client->common.input, +--- dovecot-2.4.1+dfsg1.orig/src/lib-imap/test-imap-parser.c ++++ dovecot-2.4.1+dfsg1/src/lib-imap/test-imap-parser.c +@@ -95,7 +95,7 @@ static void test_imap_parser_list_limit( + const struct imap_arg *args; + + test_begin("imap parser list limit"); +- struct imap_parser_params params = { ++ const struct imap_parser_params params = { + .list_count_limit = 2, + }; + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27858.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27858.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27858.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27858.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,31 @@ +From fcb339d39bfb19abf1bb88711112f49c1c589b1a Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +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(+) + +diff --git a/pigeonhole/src/managesieve-login/client-authenticate.c b/pigeonhole/src/managesieve-login/client-authenticate.c +index 822dae6a..5aeccd35 100644 +--- a/pigeonhole/src/managesieve-login/client-authenticate.c ++++ b/pigeonhole/src/managesieve-login/client-authenticate.c +@@ -196,6 +196,11 @@ managesieve_client_auth_read_response(struct managesieve_client *msieve_client, + 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 = +-- +2.39.5 + diff -Nru dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27859.patch dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27859.patch --- dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27859.patch 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/CVE-2026-27859.patch 2026-03-31 19:07:17.000000000 +0000 @@ -0,0 +1,100 @@ +From 4041a0cb7bbaa6faf38670ab92afc1039dd0d0d5 Mon Sep 17 00:00:00 2001 +From: Timo Sirainen +Date: Tue, 24 Feb 2026 13:11:14 +0200 +Subject: [PATCH 03/24] lib-mail: Limit the number of RFC2231 parameters that + can be parsed + +This avoids excessive CPU usage especially in result_append(). +--- + src/lib-mail/rfc2231-parser.c | 4 +++- + src/lib-mail/rfc822-parser.h | 5 +++++ + src/lib-mail/test-rfc2231-parser.c | 30 ++++++++++++++++++++++++++++++ + 3 files changed, 38 insertions(+), 1 deletion(-) + +--- dovecot-2.4.1+dfsg1.orig/src/lib-mail/rfc2231-parser.c ++++ dovecot-2.4.1+dfsg1/src/lib-mail/rfc2231-parser.c +@@ -203,7 +203,7 @@ int rfc2231_parse(struct rfc822_parser_c + struct rfc2231_parameter rfc2231_param; + const char *key, *p, *p2; + string_t *str; +- unsigned int i, j, count, next, next_idx; ++ unsigned int i, j, count, next, next_idx, params_count = 0; + bool ok, broken = FALSE; + const char *prev_replacement_str; + int ret; +@@ -221,6 +221,8 @@ int rfc2231_parse(struct rfc822_parser_c + t_array_init(&rfc2231_params_arr, 8); + str = t_str_new(64); + while ((ret = rfc822_parse_content_param(ctx, &key, str)) != 0) { ++ if (++params_count > RFC2231_MAX_PARAMS) ++ break; + if (ret < 0) { + /* try to continue anyway.. */ + broken = TRUE; +--- dovecot-2.4.1+dfsg1.orig/src/lib-mail/rfc822-parser.h ++++ dovecot-2.4.1+dfsg1/src/lib-mail/rfc822-parser.h +@@ -3,6 +3,11 @@ + + #include "unichar.h" + ++/* Maximum number of parameters to parse. After this the rest of the parameters ++ are skipped. This is to avoid excessive CPU usage that can be caused by ++ merging of these parameters. */ ++#define RFC2231_MAX_PARAMS 128 ++ + /* This can be used as a common NUL replacement character */ + #define RFC822_NUL_REPLACEMENT_STR UNICODE_REPLACEMENT_CHAR_UTF8 + +--- dovecot-2.4.1+dfsg1.orig/src/lib-mail/test-rfc2231-parser.c ++++ dovecot-2.4.1+dfsg1/src/lib-mail/test-rfc2231-parser.c +@@ -1,6 +1,7 @@ + /* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + + #include "lib.h" ++#include "str.h" + #include "rfc822-parser.h" + #include "rfc2231-parser.h" + #include "test-common.h" +@@ -249,6 +250,34 @@ static void test_rfc2231_parser_multi_en + run_test("rfc2231 parser invalid encoding", input, sizeof(input)-1, output); + } + ++static void test_rfc2231_parser_limits(void) ++{ ++ string_t *input = t_str_new(1024); ++ ++ test_begin("rfc2231 parser limits"); ++ str_append(input, "; "); ++ for (unsigned int i = 0; i < 1100; i++) ++ str_printfa(input, "a%u=b%u; ", i, i); ++ struct rfc822_parser_context parser; ++ const char *const *result; ++ rfc822_parser_init(&parser, str_data(input), str_len(input), NULL); ++ test_assert(rfc2231_parse(&parser, &result) == 0); ++ ++ unsigned int count = str_array_length(result); ++ test_assert(count == RFC2231_MAX_PARAMS * 2); ++ for (unsigned int i = 0; i < count; i += 2) { ++ str_truncate(input, 0); ++ str_printfa(input, "a%u", i / 2); ++ test_assert_strcmp_idx(result[i], str_c(input), i); ++ ++ str_truncate(input, 0); ++ str_printfa(input, "b%u", i / 2); ++ test_assert_strcmp_idx(result[i + 1], str_c(input), i); ++ } ++ rfc822_parser_deinit(&parser); ++ test_end(); ++} ++ + int main(void) + { + static void (*const test_functions[])(void) = { +@@ -264,6 +293,7 @@ int main(void) + test_rfc2231_parser_encodings, + test_rfc2231_parser_utf8_encoding, + test_rfc2231_parser_multi_encodings, ++ test_rfc2231_parser_limits, + NULL + }; + return test_run(test_functions); diff -Nru dovecot-2.4.1+dfsg1/debian/patches/series dovecot-2.4.1+dfsg1/debian/patches/series --- dovecot-2.4.1+dfsg1/debian/patches/series 2026-03-06 14:36:12.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/patches/series 2026-03-31 19:07:17.000000000 +0000 @@ -32,3 +32,28 @@ bug1121000_dovecot-ldap_Crash_if_iterate_filter_is_set_but_iterate_fields_is_not_set.patch 0001-trash-Use-mailbox-event-in-trash_try_mailbox-for-set.patch acl-Fix-crash-when-group-ACLs-are-used-but-user-s-ac.patch +CVE-2025-59028.patch +CVE-2025-59031.patch +CVE-2025-59032.patch +CVE-2026-24031-27860-1.patch +CVE-2026-24031-27860-2.patch +CVE-2026-24031-27860-3.patch +CVE-2026-24031-27860-4.patch +CVE-2026-24031-27860-5.patch +CVE-2026-24031-27860-6.patch +CVE-2026-24031-27860-7.patch +CVE-2026-24031-27860-8.patch +CVE-2026-27855-1.patch +CVE-2026-27855-2.patch +CVE-2026-27855-3.patch +CVE-2026-27855-4.patch +CVE-2026-27856-1.patch +CVE-2026-27856-2.patch +CVE-2026-27856-3.patch +CVE-2026-27857-1.patch +CVE-2026-27857-2.patch +CVE-2026-27857-3.patch +CVE-2026-27857-4.patch +CVE-2026-27857-5.patch +CVE-2026-27858.patch +CVE-2026-27859.patch diff -Nru dovecot-2.4.1+dfsg1/debian/rules dovecot-2.4.1+dfsg1/debian/rules --- dovecot-2.4.1+dfsg1/debian/rules 2026-03-05 01:13:48.000000000 +0000 +++ dovecot-2.4.1+dfsg1/debian/rules 2026-03-31 18:37:57.000000000 +0000 @@ -191,7 +191,6 @@ $(MAKE) install DESTDIR=$(CORE_DIR) $(MAKE) -C $(PIGEONHOLE_DIR) install DESTDIR=$(CORE_DIR) rm `find $(CURDIR)/debian -name '*.la'` - rm $(CORE_DIR)/usr/lib/dovecot/decode2text.sh override_dh_install: # dh_auto_install has installed everything in the dovecot-core package.