Bug#1142925: trixie-pu: package cyrus-imapd/3.10.2-1+deb13u2

Edmund Lodewijks <[email protected]>
Newsgroups gmane.linux.debian.devel.release
Message-ID <7dc24e89-f6bc-4095-84bd-b428d7e3f0dc__22374.1850511701$1788257997$gmane$org@proteamail.com>
Control: tags -1 + patch

Hi Release Team,

Update / ping on this issue: trixie is still vulnerable to all nine
CVEs, and the upload has not been made. Attached is an updated debdiff
against 3.10.2-1+deb13u1 (current version in trixie-proposed-updates).

The maintainer, Xavier Guimard, has reviewed and merged this into the
packaging repository:
https://salsa.debian.org/debian/cyrus-imapd/-/merge_requests/14

Changes since the debdiff Bastian posted on 2026-07-28: DEP-3 metadata
on all nine patches, and the upstream Cassandane regression tests that
the original omitted (it carried tests for only three of the nine
fixes).

* Risks
Low. All nine are upstream fixes cherry-picked from 3.10.3, each adding
a single missing ACL check (or a bounds check, for CVE-2026-47088). No
packaging, ABI or configuration changes. Diff is +1065/-0, confined to
debian/changelog, debian/patches/series and nine new patches.

* Tests
Built in a clean trixie chroot (sbuild) on amd64. No runtime testing was
done: Each fix ships its upstream Cassandane regression test, but those
are not exercised yet, because the autopkgtest pulls the archived
upstream cassandane repo rather than the in-tree suite (#1145447, being
fixed separately).

Fixed in unstable since 3.12.3-4.

Thank you,

     Edmund


-- 
Edmund Lodewijks <[email protected]>
TZ: UTC+2 / GMT+2
cyrus-imapd_deb13u2.debdiff (text/plain, 45.3 KB)
diff -Nru cyrus-imapd-3.10.2/debian/changelog cyrus-imapd-3.10.2/debian/changelog
--- cyrus-imapd-3.10.2/debian/changelog	2026-07-12 21:29:05.000000000 +0200
+++ cyrus-imapd-3.10.2/debian/changelog	2026-08-02 12:00:00.000000000 +0200
@@ -1,3 +1,31 @@
+cyrus-imapd (3.10.2-1+deb13u2) trixie; urgency=high
+
+  * Backport security fixes from upstream 3.10.3 (Closes: #1142925)
+    (https://www.cyrusimap.org/imap/download/release-notes/3.10/x/3.10.3.html):
+    - CVE-2026-47084: LOCALDELETE bypassed ACL checks, allowing non-admin
+      users to delete mailboxes without permission.
+    - CVE-2026-47086: GENURLAUTH issued URLAUTH tokens without checking
+      ACL_READ on the target mailbox.
+    - CVE-2026-47087: URLAUTH tokens kept working after the authorizer's
+      access was revoked.
+    - CVE-2026-47081: XAPPLEPUSHSERVICE allowed probing for mailbox
+      existence and hijacking push notifications on other users' folders.
+    - CVE-2026-47089: LISTRIGHTS was not restricted to users with admin
+      access on the target mailbox.
+    - CVE-2026-47085: URLAUTH tokens could be forged via a predictable
+      empty mboxkey.
+    - CVE-2026-47083: MULTISEARCH/ESEARCH allowed a cross-user folder and
+      content enumeration oracle.
+    - CVE-2026-47088: heap out-of-bounds read when parsing nested MIME
+      comments in RFC 822 headers.
+    - CVE-2026-47082: vacation "fcc" delivery skipped the ACL check on the
+      destination mailbox.
+  * This revision adds DEP-3 metadata to the new patches and restores
+    upstream Cassandane regression tests that were missing from the initial
+    debdiff (thanks, Codin!) for several of the fixes.
+
+ -- Edmund Lodewijks <[email protected]>  Sun, 02 Aug 2026 12:00:00 +0200
+
 cyrus-imapd (3.10.2-1+deb13u1) trixie; urgency=medium
 
   * http_jmap: allow JMAP EventSource without WebSocket/wslay; backport
diff -Nru cyrus-imapd-3.10.2/debian/patches/0020-lmtp_sieve.c-enforce-ACL-on-vacation-fcc-destination.patch cyrus-imapd-3.10.2/debian/patches/0020-lmtp_sieve.c-enforce-ACL-on-vacation-fcc-destination.patch
--- cyrus-imapd-3.10.2/debian/patches/0020-lmtp_sieve.c-enforce-ACL-on-vacation-fcc-destination.patch	1970-01-01 02:00:00.000000000 +0200
+++ cyrus-imapd-3.10.2/debian/patches/0020-lmtp_sieve.c-enforce-ACL-on-vacation-fcc-destination.patch	2026-08-02 12:00:00.000000000 +0200
@@ -0,0 +1,101 @@
+Description: lmtp_sieve.c: enforce ACL on vacation :fcc destination (CVE-2026-47082)
+ An initial debdiff for this CVE batch was submitted by Codin
+ <[email protected]> and sponsored/reviewed by Bastian Germann
+ <[email protected]> in Debian bug #1142925. This revision adds DEP-3
+ metadata and, where missing from that debdiff, folds in the matching
+ upstream Cassandane regression test(s).
+ .
+ This is CYR-2892, CVE-2026-47082.
+ .
+ Enforce *some* ACL check when delivering the fcc for a vacation message!
+ Rather than pass aclcheck=0, pass ACL_INSERT: the script owner must be
+ able to write to the target mailbox.
+ .
+ This problem was reported by Michael Lynch (mtlynch.io).
+ .
+ Co-authored-by: Claude <[email protected]>
+Author: Ricardo Signes <[email protected]>
+Origin: upstream, https://github.com/cyrusimap/cyrus-imapd/commit/db6d646d00
+Bug: https://security-tracker.debian.org/tracker/CVE-2026-47082
+Bug-Debian: https://bugs.debian.org/1142925
+Bug-CVE: CVE-2026-47082
+Forwarded: not-needed
+Last-Update: 2026-07-25
+
+diff --git a/cassandane/tiny-tests/Sieve/vacation_with_fcc_otheruser_denied b/cassandane/tiny-tests/Sieve/vacation_with_fcc_otheruser_denied
+new file mode 100644
+index 0000000..ed9ff9a
+--- /dev/null
++++ b/cassandane/tiny-tests/Sieve/vacation_with_fcc_otheruser_denied
+@@ -0,0 +1,58 @@
++#!perl
++use Cassandane::Tiny;
++
++sub test_vacation_with_fcc_otheruser_denied
++    :min_version_3_1
++    :NoAltNameSpace
++    :want_smtpdaemon
++{
++    my ($self) = @_;
++
++    my $target = "user.other.target";
++
++    xlog $self, "Create victim user 'other' and a target folder";
++    $self->{instance}->create_user('other');
++
++    my $admintalk = $self->{adminstore}->get_client();
++    $admintalk->create($target)
++        or die "Cannot create $target: $@";
++
++    xlog $self, "Give cassandane read-only access (no 'i'/'p' rights)";
++    $admintalk->setacl($target, 'cassandane' => 'lrs');
++    $self->assert_str_equals('ok',
++        $admintalk->get_last_completion_response());
++
++    xlog $self, "Confirm cassandane cannot APPEND directly to $target";
++    my $talk = $self->{store}->get_client();
++    $talk->append($target,
++        "From: nobody\@example.com\r\n"
++        . "Subject: direct-append\r\n\r\n"
++        . "denied\r\n");
++    $self->assert_str_equals('no',
++        $talk->get_last_completion_response());
++
++    xlog $self, "Install a vacation rule that :fcc's into the victim's folder";
++    $self->{instance}->install_sieve_script(<<EOF
++require ["vacation", "fcc"];
++
++vacation :fcc "$target" :days 1 :addresses ["cassandane\@example.com"] text:
++I am out of the office today. I will answer your email as soon as I can.
++.
++;
++EOF
++    );
++
++    xlog $self, "Deliver a message to trigger the vacation auto-reply";
++    my $msg1 = $self->{gen}->generate(
++        subject => "Message 1",
++        to => Cassandane::Address->new(localpart => 'cassandane',
++                                       domain => 'example.com'),
++    );
++    $self->{instance}->deliver($msg1);
++
++    xlog $self, "Check that nothing was filed into the victim's folder";
++    $admintalk->select($target);
++    $self->assert_str_equals('ok',
++        $admintalk->get_last_completion_response());
++    $self->assert_num_equals(0, $admintalk->get_response_code('exists'));
++}
+diff --git a/imap/lmtp_sieve.c b/imap/lmtp_sieve.c
+index f13a266..b85bf1a 100644
+--- a/imap/lmtp_sieve.c
++++ b/imap/lmtp_sieve.c
+@@ -1931,7 +1931,7 @@ static void do_fcc(script_data_t *sdata, sieve_fileinto_context_t *fcc,
+     }
+     if (!r) {
+         r = append_setup(&as, intname, userid, sdata->authstate,
+-                         0, NULL, NULL, 0, EVENT_MESSAGE_APPEND);
++                         ACL_INSERT, NULL, NULL, 0, EVENT_MESSAGE_APPEND);
+     }
+     if (!r) {
+         struct stagemsg *stage;
diff -Nru cyrus-imapd-3.10.2/debian/patches/0021-imap-message.c-fix-heap-exposure-in-nested-MIME-comm.patch cyrus-imapd-3.10.2/debian/patches/0021-imap-message.c-fix-heap-exposure-in-nested-MIME-comm.patch
--- cyrus-imapd-3.10.2/debian/patches/0021-imap-message.c-fix-heap-exposure-in-nested-MIME-comm.patch	1970-01-01 02:00:00.000000000 +0200
+++ cyrus-imapd-3.10.2/debian/patches/0021-imap-message.c-fix-heap-exposure-in-nested-MIME-comm.patch	2026-08-02 12:00:00.000000000 +0200
@@ -0,0 +1,145 @@
+Description: imap/message.c: fix heap exposure in nested MIME comment parsing (CVE-2026-47088)
+ An initial debdiff for this CVE batch was submitted by Codin
+ <[email protected]> and sponsored/reviewed by Bastian Germann
+ <[email protected]> in Debian bug #1142925. This revision adds DEP-3
+ metadata and, where missing from that debdiff, folds in the matching
+ upstream Cassandane regression test(s).
+ .
+ This is CYR-2882, CVE-2026-47088.
+ .
+ Fix out-of-bounds read in message_parse_rfc822space() when parsing RFC
+ 822 comments that end with backslash. The backslash case would increment
+ p past a NUL terminator, and the subsequent loop increment would read
+ into adjacent heap memory.
+ .
+ Add bounds check after backslash handling to prevent reading past the
+ end of the buffer.
+ .
+ This problem was reported by Michael Lynch (mtlynch.io).
+ .
+ Co-Authored-By: Claude <[email protected]>
+Author: Ricardo Signes <[email protected]>
+Origin: upstream, https://github.com/cyrusimap/cyrus-imapd/commit/23495997b5
+Bug: https://security-tracker.debian.org/tracker/CVE-2026-47088
+Bug-Debian: https://bugs.debian.org/1142925
+Bug-CVE: CVE-2026-47088
+Forwarded: not-needed
+Last-Update: 2026-07-25
+
+diff --git a/cassandane/Cassandane/Cyrus/MIME.pm b/cassandane/Cassandane/Cyrus/MIME.pm
+new file mode 100644
+index 0000000..58df5a2
+--- /dev/null
++++ b/cassandane/Cassandane/Cyrus/MIME.pm
+@@ -0,0 +1,40 @@
++# SPDX-License-Identifier: BSD-3-Clause-CMU
++# See COPYING file at the root of the distribution for more details.
++
++package Cassandane::Cyrus::MIME;
++use strict;
++use warnings;
++use Data::Dumper;
++
++use base qw(Cassandane::Cyrus::TestCase);
++use Cassandane::Util::Log;
++
++sub new
++{
++    my ($class, @args) = @_;
++
++    my $config = Cassandane::Config->default()->clone();
++
++    my $self = $class->SUPER::new({
++        config => $config,
++        services => [ 'imap' ]
++    }, @args);
++
++    return $self;
++}
++
++sub set_up
++{
++    my ($self) = @_;
++    $self->SUPER::set_up();
++}
++
++sub tear_down
++{
++    my ($self) = @_;
++    $self->SUPER::tear_down();
++}
++
++use Cassandane::Tiny::Loader 'tiny-tests/MIME';
++
++1;
+diff --git a/cassandane/tiny-tests/MIME/nested_comment_oob b/cassandane/tiny-tests/MIME/nested_comment_oob
+new file mode 100644
+index 0000000..b47f6b1
+--- /dev/null
++++ b/cassandane/tiny-tests/MIME/nested_comment_oob
+@@ -0,0 +1,50 @@
++#!perl
++use Cassandane::Tiny;
++
++sub test_nested_comment_oob
++{
++    my ($self) = @_;
++
++    my $talk = $self->{store}->get_client();
++
++    # Create a multipart message where the vulnerability is in a nested part.
++    # Based on the PoC description, this reproduces the backslash handling bug
++    # in message_parse_rfc822space() when parsing nested Content-Type headers.
++    # The malformed header must be the last content to ensure backslash is final byte.
++    my $msg = <<'EOF';
++From: [email protected]
++To: [email protected]
++Subject: Test heap disclosure
++Content-Type: multipart/mixed; boundary="BOUNDARY"
++
++--BOUNDARY
++Content-Type: text/plain
++
++This is a normal part.
++
++--BOUNDARY
++Content-Type: text/plain (\
++EOF
++
++    # Remove trailing newline so backslash is the final byte before NUL
++    chomp $msg;
++    $msg =~ s/\r?\n/\r\n/gs;
++
++    # Append the message
++    $talk->append('INBOX', $msg) || die $@;
++
++    # Select the mailbox
++    $talk->select('INBOX');
++    $self->assert_str_equals('ok', $talk->get_last_completion_response());
++
++    # Fetch BODYSTRUCTURE - this exercises the vulnerable MIME parsing code
++    # In vulnerable versions, this may crash or leak heap data as MIME parameters
++    my $response = $talk->fetch('1', 'BODYSTRUCTURE');
++    $self->assert_str_equals('ok', $talk->get_last_completion_response());
++
++    # Log the bodystructure for manual inspection of any anomalies
++    # In a release build with heap spray, unexpected parameters may appear
++    if ($response && $response->{1} && $response->{1}->{bodystructure}) {
++        xlog $self, "BODYSTRUCTURE: " . Data::Dumper::Dumper($response->{1}->{bodystructure});
++    }
++}
+diff --git a/imap/message.c b/imap/message.c
+index a5b633e..f18b1c6 100644
+--- a/imap/message.c
++++ b/imap/message.c
+@@ -1768,6 +1768,10 @@ static void message_parse_rfc822space(const char **s)
+ 
+                 case '\\':
+                     p++;
++                    if (*p == '\0') {
++                        *s = 0;     /* backslash at end of string */
++                        return;
++                    }
+                     break;
+ 
+                 case '(':
diff -Nru cyrus-imapd-3.10.2/debian/patches/0022-imapd-MULTISEARCH-must-check-ACL_READ-on-each-mailbo.patch cyrus-imapd-3.10.2/debian/patches/0022-imapd-MULTISEARCH-must-check-ACL_READ-on-each-mailbo.patch
--- cyrus-imapd-3.10.2/debian/patches/0022-imapd-MULTISEARCH-must-check-ACL_READ-on-each-mailbo.patch	1970-01-01 02:00:00.000000000 +0200
+++ cyrus-imapd-3.10.2/debian/patches/0022-imapd-MULTISEARCH-must-check-ACL_READ-on-each-mailbo.patch	2026-08-02 12:00:00.000000000 +0200
@@ -0,0 +1,96 @@
+Description: imapd: MULTISEARCH must check ACL_READ on each mailbox (CVE-2026-47083)
+ An initial debdiff for this CVE batch was submitted by Codin
+ <[email protected]> and sponsored/reviewed by Bastian Germann
+ <[email protected]> in Debian bug #1142925. This revision adds DEP-3
+ metadata and, where missing from that debdiff, folds in the matching
+ upstream Cassandane regression test(s).
+ .
+ This is CYR-2881, CVE-2026-47083.
+ .
+ multisearch_cb opened caller-supplied mailboxes from
+ MAILBOXES/SUBTREE/SUBTREE-ONE and ran index_search on them without
+ checking whether the caller could read the mailbox. SELECT has the
+ equivalent gate (imapd.c:5001); MULTISEARCH did not.
+ .
+ The effect is a cross-user read oracle: SUBTREE on another user's
+ account returns one `* ESEARCH ... MAILBOX "..."` line per folder
+ (folder enumeration for free), and BODY/SUBJECT predicates then probe
+ the contents one search at a time. An attacker only needs to be able
+ to name a target mailbox.
+ .
+ This problem was reported by Michael Lynch (mtlynch.io).
+ .
+ Co-Authored-By: Claude <[email protected]>
+Author: Ricardo Signes <[email protected]>
+Origin: upstream, https://github.com/cyrusimap/cyrus-imapd/commit/0534c4e30a
+Bug: https://security-tracker.debian.org/tracker/CVE-2026-47083
+Bug-Debian: https://bugs.debian.org/1142925
+Bug-CVE: CVE-2026-47083
+Forwarded: not-needed
+Last-Update: 2026-07-25
+
+diff --git a/cassandane/Cassandane/Cyrus/Search.pm b/cassandane/Cassandane/Cyrus/Search.pm
+index 19e16fa..d9e5e9e 100644
+--- a/cassandane/Cassandane/Cyrus/Search.pm
++++ b/cassandane/Cassandane/Cyrus/Search.pm
+@@ -757,4 +757,44 @@ sub test_uidsearch_empty
+     $self->assert_str_equals('0', $results[0][3]);
+ }
+ 
++sub test_multisearch_cross_user
++    :NoAltNamespace
++{
++    my ($self) = @_;
++
++    my $admintalk = $self->{adminstore}->get_client;
++    my $talk = $self->{store}->get_client;
++
++    # victim has a private subfolder with a message. cassandane has no
++    # explicit grant on it, only whatever the "anyone p" default provides --
++    # i.e. ACL_POST, which is not enough to run a search.
++    $self->{instance}->create_user('victim');
++
++    $admintalk->create('user.victim.private')
++        || die "create: " . $admintalk->get_last_error;
++
++    $admintalk->append(
++        'user.victim.private',
++        "From: t\@example.com\r\nSubject: secret\r\n\r\nconfidential body",
++    ) || die "append: " . $admintalk->get_last_error;
++
++    # Check our assumptions: default user has no access to victim's private
++    # folder.
++    $talk->select('user.victim.private');
++    $self->assert_str_equals('no', $talk->get_last_completion_response);
++
++    # The actual test: assert that cassandane user can't find anything by
++    # ESEARCHing victim's private mailbox.
++    my @esearch_responses;
++    $talk->_imap_cmd(
++        'ESEARCH', 0,
++        { esearch => sub { push @esearch_responses, $_[1] } },
++        'IN', '(MAILBOXES "user.victim.private")',
++        'RETURN', '(ALL)', 'ALL',
++    );
++
++    $self->assert_str_equals('ok', $talk->get_last_completion_response);
++    $self->assert_deep_equals([], \@esearch_responses);
++}
++
+ 1;
+diff --git a/imap/imapd.c b/imap/imapd.c
+index f7be33e..4edd0bf 100644
+--- a/imap/imapd.c
++++ b/imap/imapd.c
+@@ -6076,6 +6076,11 @@ static int multisearch_cb(const mbentry_t *mbentry, void *rock)
+         hash_lookup(mbentry->name, &mrock->mailboxes))
+         return 0;
+ 
++    /* Skip mailboxes the caller can't read. */
++    if (!imapd_userisadmin &&
++        !(cyrus_acl_myrights(imapd_authstate, mbentry->acl) & ACL_READ))
++        return 0;
++
+     switch (mrock->filter) {
+     case SEARCH_SOURCE_INBOXES:
+         /* Only allow user's INBOX or those postable by anonymous */
diff -Nru cyrus-imapd-3.10.2/debian/patches/0023-imapd-reject-URLFETCH-when-no-mboxkey-exists-for-the.patch cyrus-imapd-3.10.2/debian/patches/0023-imapd-reject-URLFETCH-when-no-mboxkey-exists-for-the.patch
--- cyrus-imapd-3.10.2/debian/patches/0023-imapd-reject-URLFETCH-when-no-mboxkey-exists-for-the.patch	1970-01-01 02:00:00.000000000 +0200
+++ cyrus-imapd-3.10.2/debian/patches/0023-imapd-reject-URLFETCH-when-no-mboxkey-exists-for-the.patch	2026-08-02 12:00:00.000000000 +0200
@@ -0,0 +1,121 @@
+Description: imapd: reject URLFETCH when no mboxkey exists for the mailbox (CVE-2026-47085)
+ An initial debdiff for this CVE batch was submitted by Codin
+ <[email protected]> and sponsored/reviewed by Bastian Germann
+ <[email protected]> in Debian bug #1142925. This revision adds DEP-3
+ metadata and, where missing from that debdiff, folds in the matching
+ upstream Cassandane regression test(s).
+ .
+ This is CYR-2876, CVE-2026-47085.
+ .
+ Instead of treating "there's no mboxkey.db entry for this folder" as
+ indicating a zero-length key, treat a missing mboxkey entry as a
+ validation failure.
+ .
+ (Also close the mboxkey db on the previously-unclosed error paths.)
+ .
+ This problem reported by Matthew Horsfall.
+ .
+ Co-Authored-By: Claude <[email protected]>
+ .
+ Cherry-picked from upstream commits:
+   e5a1055614 cassandane: test for URLFETCH token forgery with empty mboxkey
+   1d39d32c3b imapd: reject URLFETCH when no mboxkey exists for the mailbox
+Author: Ricardo Signes <[email protected]>
+Origin: upstream, https://github.com/cyrusimap/cyrus-imapd/commit/1d39d32c3b
+Bug: https://security-tracker.debian.org/tracker/CVE-2026-47085
+Bug-Debian: https://bugs.debian.org/1142925
+Bug-CVE: CVE-2026-47085
+Forwarded: not-needed
+Last-Update: 2026-07-25
+
+Index: cyrus-imapd/cassandane/Cassandane/Cyrus/URLAuth.pm
+===================================================================
+--- cyrus-imapd.orig/cassandane/Cassandane/Cyrus/URLAuth.pm
++++ cyrus-imapd/cassandane/Cassandane/Cyrus/URLAuth.pm
+@@ -44,6 +44,7 @@ use Cwd qw(abs_path);
+ use File::Path qw(mkpath);
+ use DateTime;
+ use Data::Dumper;
++use Digest::SHA qw(hmac_sha1_hex);
+ 
+ use lib '.';
+ use base qw(Cassandane::Cyrus::TestCase);
+@@ -161,4 +162,57 @@ sub test_urlfetch
+     $self->assert_str_equals($data, "ody");
+ }
+ 
++sub test_forged_empty_key
++    :UnixHierarchySep :AltNamespace
++{
++    my ($self) = @_;
++
++    # The victim has an INBOX and a second folder.  The secret we don't want
++    # leaked lives in the second folder.
++    my $talk = $self->{store}->get_client;
++    $talk->create("Archive") or die "create Archive: " . $talk->get_last_error;
++
++    $self->{store}->set_folder("INBOX");
++    $self->make_message("Public INBOX message");
++
++    $self->{store}->set_folder("Archive");
++    $self->make_message("Secret archived message");
++
++    # Cause the victim's mboxkey.db to be created, but with an entry only for
++    # the INBOX -- *not* for the Archive folder we're about to attack.  An
++    # mboxkey.db appears in the wild whenever a user has ever issued a
++    # GENURLAUTH (or a RESETKEY for a specific mailbox).
++    my $seed;
++    $talk->_imap_cmd(
++        'genurlauth', 0,
++        { genurlauth => sub { $seed = $_[1]->[0] } },
++        'imap://[email protected]/INBOX/;uid=1;urlauth=user+cassandane',
++        'INTERNAL',
++    );
++    $self->assert_not_null($seed, 'victim should be able to URLAUTH their own INBOX');
++
++    # Now the attacker forges a token for the *Archive* folder.  Because
++    # mboxkey_read() returns {key=NULL, keylen=0} for any mailbox it has never
++    # stored, cmd_urlfetch computes HMAC-SHA1 with an empty key -- which is
++    # entirely attacker-computable.
++    $self->{instance}->create_user('attacker');
++
++    my $rump = 'imap://[email protected]/Archive/;uid=1;urlauth=user+attacker';
++    my $token = '00' . hmac_sha1_hex($rump, '');
++    my $forged = "$rump:internal:$token";
++
++    my $svc = $self->{instance}->get_service('imap');
++    my $store = $svc->create_store(username => 'attacker');
++    my $attacker = $store->get_client();
++
++    my $stolen;
++    $attacker->_imap_cmd(
++        'urlfetch', 0,
++        { urlfetch => sub { $stolen = $_[1]->[1] } },
++        $forged,
++    );
++
++    $self->assert_null($stolen);
++}
++
+ 1;
+Index: cyrus-imapd/imap/imapd.c
+===================================================================
+--- cyrus-imapd.orig/imap/imapd.c
++++ cyrus-imapd/imap/imapd.c
+@@ -14061,7 +14061,15 @@ static void cmd_urlfetch(char *tag)
+                 if (r) break;
+ 
+                 r = mboxkey_read(mboxkey_db, intname, &key, &keylen);
+-                if (r) break;
++                if (!r && (!key || !keylen)) {
++                    /* If there's no key, we can't possibly validate against
++                     * it! */
++                    r = IMAP_BADURL;
++                }
++                if (r) {
++                    mboxkey_close(mboxkey_db);
++                    break;
++                }
+ 
+                 HMAC(EVP_sha1(), key, keylen, (unsigned char *) arg.s,
+                      url.urlauth.rump_len, vtoken, &vtoken_len);
diff -Nru cyrus-imapd-3.10.2/debian/patches/0024-imapd-LISTRIGHTS-requires-admin-rights.patch cyrus-imapd-3.10.2/debian/patches/0024-imapd-LISTRIGHTS-requires-admin-rights.patch
--- cyrus-imapd-3.10.2/debian/patches/0024-imapd-LISTRIGHTS-requires-admin-rights.patch	1970-01-01 02:00:00.000000000 +0200
+++ cyrus-imapd-3.10.2/debian/patches/0024-imapd-LISTRIGHTS-requires-admin-rights.patch	2026-08-02 12:00:00.000000000 +0200
@@ -0,0 +1,85 @@
+Description: imapd: LISTRIGHTS requires admin rights (CVE-2026-47089)
+ An initial debdiff for this CVE batch was submitted by Codin
+ <[email protected]> and sponsored/reviewed by Bastian Germann
+ <[email protected]> in Debian bug #1142925. This revision adds DEP-3
+ metadata and, where missing from that debdiff, folds in the matching
+ upstream Cassandane regression test(s).
+ .
+ This is CYR-2872, CVE-2026-47089.
+ .
+ cmd_listrights was refusing only when the caller had *no* rights at
+ all on the target mailbox, when it should've been requiring admin
+ rights.
+ .
+ With this patch, we'll properly require ACL_ADMIN (owners and admins
+ still pass through), matching the check cmd_getacl already uses.  When
+ the caller has ACL_LOOKUP but not ACL_ADMIN, return PERMISSION_DENIED;
+ otherwise hide the mailbox as NONEXISTENT.
+ .
+ This problem reported by Matthew Horsfall.
+ .
+ Co-Authored-By: Claude <[email protected]>
+ .
+ Cherry-picked from upstream commits:
+   5f406195f7 cassandane: test that LISTRIGHTS requires admin rights
+   5c97794e52 imapd: LISTRIGHTS requires admin rights
+Author: Ricardo Signes <[email protected]>
+Origin: upstream, https://github.com/cyrusimap/cyrus-imapd/commit/5c97794e52
+Bug: https://security-tracker.debian.org/tracker/CVE-2026-47089
+Bug-Debian: https://bugs.debian.org/1142925
+Bug-CVE: CVE-2026-47089
+Forwarded: not-needed
+Last-Update: 2026-07-25
+
+diff --git a/cassandane/Cassandane/Cyrus/ACL.pm b/cassandane/Cassandane/Cyrus/ACL.pm
+index 62fef8e..64ca817 100644
+--- a/cassandane/Cassandane/Cyrus/ACL.pm
++++ b/cassandane/Cassandane/Cyrus/ACL.pm
+@@ -270,6 +270,30 @@ sub test_setacl_badrights
+     $self->assert_deep_equals($origacl, $newacl);
+ }
+ 
++sub test_listrights_other_user
++{
++    my ($self) = @_;
++
++    my $talk = $self->{store}->get_client;
++
++    # Create a victim user.  We grant the attacker no explicit rights on the
++    # victim's mailbox: the default ACL "anyone p" is enough to expose the
++    # bug.  (LISTRIGHTS gates only on "has any right at all", where it ought
++    # to require ACL_ADMIN; ACL_POST from the anyone grant is sufficient to
++    # slip past.)
++    $self->{instance}->create_user('victim');
++
++    my $seen;
++    $talk->_imap_cmd(
++        'LISTRIGHTS', 0,
++        { listrights => sub { $seen = 1 } },
++        'Other Users.victim', 'victim',
++    );
++
++    $self->assert_str_equals('no', $talk->get_last_completion_response);
++    $self->assert_null($seen);
++}
++
+ # see also LDAP.pm for groupid tests
+ 
+ 1;
+diff --git a/imap/imapd.c b/imap/imapd.c
+index 6aca6f4..703d835 100644
+--- a/imap/imapd.c
++++ b/imap/imapd.c
+@@ -8520,9 +8520,10 @@ static void cmd_listrights(char *tag, char *name, char *identifier)
+     if (!r) {
+         rights = cyrus_acl_myrights(imapd_authstate, mbentry->acl);
+ 
+-        if (!rights && !imapd_userisadmin &&
++        if (!(rights & ACL_ADMIN) && !imapd_userisadmin &&
+             !mboxname_userownsmailbox(imapd_userid, intname)) {
+-            r = IMAP_MAILBOX_NONEXISTENT;
++            r = (rights & ACL_LOOKUP) ?
++                IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
+         }
+     }
+ 
diff -Nru cyrus-imapd-3.10.2/debian/patches/0025-imapd-require-read-access-for-XAPPLEPUSHSERVICE-mail.patch cyrus-imapd-3.10.2/debian/patches/0025-imapd-require-read-access-for-XAPPLEPUSHSERVICE-mail.patch
--- cyrus-imapd-3.10.2/debian/patches/0025-imapd-require-read-access-for-XAPPLEPUSHSERVICE-mail.patch	1970-01-01 02:00:00.000000000 +0200
+++ cyrus-imapd-3.10.2/debian/patches/0025-imapd-require-read-access-for-XAPPLEPUSHSERVICE-mail.patch	2026-08-02 12:00:00.000000000 +0200
@@ -0,0 +1,130 @@
+Description: imapd: require read access for XAPPLEPUSHSERVICE mailboxes (CVE-2026-47081)
+ An initial debdiff for this CVE batch was submitted by Codin
+ <[email protected]> and sponsored/reviewed by Bastian Germann
+ <[email protected]> in Debian bug #1142925. This revision adds DEP-3
+ metadata and, where missing from that debdiff, folds in the matching
+ upstream Cassandane regression test(s).
+ .
+ This is CYR-2871, CVE-2026-47081.
+ .
+ Only accept a mailbox if the caller is its owner, an admin, or holds
+ ACL_READ. Anything else is silently skipped (no echo, no event), which
+ also preserves the existing "missing" behaviour for non-existent names.
+ .
+ This problem reported by Matthew Horsfall.
+ .
+ Co-Authored-By: Claude <[email protected]>
+ .
+ Cherry-picked from upstream commits:
+   1044c0b13b cassandane: test XAPPLEPUSHSERVICE leaks other users' folders
+   50f77bad30 imapd: require read access for XAPPLEPUSHSERVICE mailboxes
+Author: Ricardo Signes <[email protected]>
+Origin: upstream, https://github.com/cyrusimap/cyrus-imapd/commit/50f77bad30
+Bug: https://security-tracker.debian.org/tracker/CVE-2026-47081
+Bug-Debian: https://bugs.debian.org/1142925
+Bug-CVE: CVE-2026-47081
+Forwarded: not-needed
+Last-Update: 2026-07-25
+
+diff --git a/cassandane/Cassandane/Cyrus/XApplePushService.pm b/cassandane/Cassandane/Cyrus/XApplePushService.pm
+new file mode 100644
+index 0000000..497d505
+--- /dev/null
++++ b/cassandane/Cassandane/Cyrus/XApplePushService.pm
+@@ -0,0 +1,40 @@
++# SPDX-License-Identifier: BSD-3-Clause-CMU
++# See COPYING file at the root of the distribution for more details.
++
++package Cassandane::Cyrus::XApplePushService;
++use strict;
++use warnings;
++
++use base qw(Cassandane::Cyrus::TestCase);
++use Cassandane::Util::Log;
++
++sub new
++{
++    my ($class, @args) = @_;
++
++    my $config = Cassandane::Config->default()->clone();
++    $config->set(event_groups => 'applepushservice');
++    $config->set(aps_topic => 'mail');
++
++    return $class->SUPER::new({
++        adminstore => 1,
++        config => $config,
++        services => ['imap'],
++    }, @args);
++}
++
++sub set_up
++{
++    my ($self) = @_;
++    $self->SUPER::set_up();
++}
++
++sub tear_down
++{
++    my ($self) = @_;
++    $self->SUPER::tear_down();
++}
++
++use Cassandane::Tiny::Loader 'tiny-tests/XApplePushService';
++
++1;
+diff --git a/cassandane/tiny-tests/XApplePushService/leak_other_user b/cassandane/tiny-tests/XApplePushService/leak_other_user
+new file mode 100644
+index 0000000..ec75872
+--- /dev/null
++++ b/cassandane/tiny-tests/XApplePushService/leak_other_user
+@@ -0,0 +1,34 @@
++#!perl
++use Cassandane::Tiny;
++
++sub test_leak_other_user
++{
++    my ($self) = @_;
++
++    my $admintalk = $self->{adminstore}->get_client;
++    my $talk = $self->{store}->get_client;
++
++    # Create a victim user and a subfolder on their account.  The attacker
++    # holds nothing on it beyond the "anyone p" default ACL.
++    $self->{instance}->create_user('victim');
++    $admintalk->create('user.victim.bah') or die "create: " . $admintalk->get_last_error;
++
++    my @responses;
++    $talk->_imap_cmd(
++        'XAPPLEPUSHSERVICE', 0,
++        { xapplepushservice => sub { push @responses, [ @{$_[1]} ] } },
++        'mailboxes',         [ 'Other Users.victim.bah' ],
++        'aps-version',       '2',
++        'aps-account-id',    'accountid',
++        'aps-device-token',  'devtoken',
++        'aps-subtopic',      'subtopic',
++    );
++
++    # Every invocation of XAPPLEPUSHSERVICE gets the trailing "aps-version"
++    # status response; that's fine.  What must not appear is a
++    # * XAPPLEPUSHSERVICE "mailbox" "<name>" echo, which both confirms the
++    # mailbox exists and (via mboxevent_set_applepushservice) subscribes
++    # the attacker's APNS device to the victim's mail.
++    my @mailbox_echoes = grep { ($_->[0] // '') eq 'mailbox' } @responses;
++    $self->assert_deep_equals([], \@mailbox_echoes);
++}
+diff --git a/imap/imapd.c b/imap/imapd.c
+index 3b3d36c..6aca6f4 100644
+--- a/imap/imapd.c
++++ b/imap/imapd.c
+@@ -14794,7 +14794,10 @@ static void cmd_xapplepushservice(const char *tag,
+         char *intname =
+             mboxname_from_external(name, &imapd_namespace, imapd_userid);
+         r = mlookup(tag, name, intname, &mbentry);
+-        if (!r && mbtype_isa(mbentry->mbtype) == MBTYPE_EMAIL) {
++        if (!r && mbtype_isa(mbentry->mbtype) == MBTYPE_EMAIL &&
++            (imapd_userisadmin ||
++             mboxname_userownsmailbox(imapd_userid, intname) ||
++             (cyrus_acl_myrights(imapd_authstate, mbentry->acl) & ACL_READ))) {
+             strarray_push(&notif_mailboxes, name);
+             if (applepushserviceargs->aps_version >= 2) {
+                 prot_puts(imapd_out, "* XAPPLEPUSHSERVICE \"mailbox\" ");
diff -Nru cyrus-imapd-3.10.2/debian/patches/0026-imapd.c-re-check-URLAUTH-authorizer-access-just-in-t.patch cyrus-imapd-3.10.2/debian/patches/0026-imapd.c-re-check-URLAUTH-authorizer-access-just-in-t.patch
--- cyrus-imapd-3.10.2/debian/patches/0026-imapd.c-re-check-URLAUTH-authorizer-access-just-in-t.patch	1970-01-01 02:00:00.000000000 +0200
+++ cyrus-imapd-3.10.2/debian/patches/0026-imapd.c-re-check-URLAUTH-authorizer-access-just-in-t.patch	2026-08-02 12:00:00.000000000 +0200
@@ -0,0 +1,127 @@
+Description: imapd.c: re-check URLAUTH authorizer access just in time (CVE-2026-47087)
+ An initial debdiff for this CVE batch was submitted by Codin
+ <[email protected]> and sponsored/reviewed by Bastian Germann
+ <[email protected]> in Debian bug #1142925. This revision adds DEP-3
+ metadata and, where missing from that debdiff, folds in the matching
+ upstream Cassandane regression test(s).
+ .
+ This is CYR-2868, CVE-2026-47087.
+ .
+ cmd_urlfetch validated the HMAC token against the authorizer's mboxkey,
+ but skipped any ACL check whenever url.urlauth.access was set.  Sure,
+ the rights existed right at that moment, but if access is revoked after
+ the URL is created, the URL needs to stop working.  We need to just
+ check access JIT.
+ .
+ Co-Authored-By: Claude <[email protected]>
+ .
+ Cherry-picked from upstream commits:
+   be5eabb4df cassandane: tighten URLAuth ACL bypass test
+   fbb130b339 cassandane: fix indenting in cassandane/tiny-tests/URLAuth/perms
+   bdf709102c cassandane: URLAUTH must re-check authorizer ACLs at fetch time
+   8380d0fb3d imapd.c: re-check URLAUTH authorizer access just in time
+Author: Ricardo Signes <[email protected]>
+Origin: upstream, https://github.com/cyrusimap/cyrus-imapd/commit/8380d0fb3d
+Bug: https://security-tracker.debian.org/tracker/CVE-2026-47087
+Bug-Debian: https://bugs.debian.org/1142925
+Bug-CVE: CVE-2026-47087
+Forwarded: not-needed
+Last-Update: 2026-07-25
+
+Index: cyrus-imapd/cassandane/Cassandane/Cyrus/URLAuth.pm
+===================================================================
+--- cyrus-imapd.orig/cassandane/Cassandane/Cyrus/URLAuth.pm
++++ cyrus-imapd/cassandane/Cassandane/Cyrus/URLAuth.pm
+@@ -215,4 +215,61 @@ sub test_forged_empty_key
+     $self->assert_null($stolen);
+ }
+ 
++sub test_revoked_access_invalidates_url
++    :UnixHierarchySep :AltNamespace
++{
++    my ($self) = @_;
++
++    my $admintalk = $self->{adminstore}->get_client;
++
++    $self->{instance}->create_user('other');
++
++    $self->make_message("Secret Message");
++
++    # Grant 'other' read+lookup so that genurlauth will mint a token.
++    $admintalk->setacl('user/cassandane', 'other', 'lrs')
++        or die "setacl: " . $admintalk->get_last_error;
++
++    my $svc = $self->{instance}->get_service('imap');
++    my $store = $svc->create_store(username => 'other');
++    my $other_talk = $store->get_client();
++
++    # 'other' mints a URLAUTH token while authorized.
++    my $url;
++    $other_talk->_imap_cmd(
++        'genurlauth', 0,
++        { genurlauth => sub { $url = $_[1]->[0] } },
++        'imap://[email protected]/Other%20Users%2fcassandane/;uid=1;urlauth=user+other',
++        'INTERNAL',
++    );
++    $self->assert_not_null($url, 'genurlauth should succeed while authorized');
++
++    # Captured value is wrapped in quotes; strip them for urlfetch.
++    (my $bare_url = $url) =~ s/\A"(.*)"\z/$1/;
++
++    # Sanity check: the token works while access is in place.
++    my $data;
++    $other_talk->_imap_cmd(
++        'urlfetch', 0,
++        { urlfetch => sub { $data = $_[1]->[1] } },
++        $bare_url,
++    );
++    $self->assert_not_null($data, 'URL should fetch while authorizer is authorized');
++
++    # Revoke 'other's access entirely.
++    $admintalk->setacl('user/cassandane', 'other', '')
++        or die "setacl: " . $admintalk->get_last_error;
++
++    # The token is cryptographically intact, but the authorizer's right to
++    # delegate the message is gone. cmd_urlfetch must re-check ACLs at fetch
++    # time and refuse, instead of trusting the token alone.
++    my $stolen;
++    $other_talk->_imap_cmd(
++        'urlfetch', 0,
++        { urlfetch => sub { $stolen = $_[1]->[1] } },
++        $bare_url,
++    );
++    $self->assert_null($stolen, 'URL must not fetch after authorizer access revoked');
++}
++
+ 1;
+Index: cyrus-imapd/imap/imapd.c
+===================================================================
+--- cyrus-imapd.orig/imap/imapd.c
++++ cyrus-imapd/imap/imapd.c
+@@ -14108,6 +14108,26 @@ static void cmd_urlfetch(char *tag)
+         }
+         if (r) goto err;
+ 
++        /* For URLAUTH-protected URLs, re-check the authorizer's ACL.  The
++         * HMAC asserts that authorization DID exist, but may have since been
++         * revoked.  We can't read the ACL via state->mailbox here: when the
++         * URL targets the currently-selected mailbox we reuse imapd_index,
++         * whose mailbox handle is closed between commands. */
++        if (url.urlauth.access) {
++            mbentry_t *authz_mbentry = NULL;
++            r = mlookup(NULL, NULL, intname, &authz_mbentry);
++            if (!r) {
++                struct auth_state *authzstate = auth_newstate(url.user);
++                int authz_rights =
++                    cyrus_acl_myrights(authzstate, authz_mbentry->acl);
++                auth_freestate(authzstate);
++                if (!(authz_rights & ACL_READ))
++                    r = IMAP_BADURL;
++            }
++            mboxlist_entry_free(&authz_mbentry);
++            if (r) goto err;
++        }
++
+         if (url.uidvalidity &&
+            (state->mailbox->i.uidvalidity != url.uidvalidity)) {
+             r = IMAP_BADURL;
diff -Nru cyrus-imapd-3.10.2/debian/patches/0027-imapd-require-read-access-on-mailbox-in-GENURLAUTH.patch cyrus-imapd-3.10.2/debian/patches/0027-imapd-require-read-access-on-mailbox-in-GENURLAUTH.patch
--- cyrus-imapd-3.10.2/debian/patches/0027-imapd-require-read-access-on-mailbox-in-GENURLAUTH.patch	1970-01-01 02:00:00.000000000 +0200
+++ cyrus-imapd-3.10.2/debian/patches/0027-imapd-require-read-access-on-mailbox-in-GENURLAUTH.patch	2026-08-02 12:00:00.000000000 +0200
@@ -0,0 +1,107 @@
+Description: imapd: require read access on mailbox in GENURLAUTH (CVE-2026-47086)
+ An initial debdiff for this CVE batch was submitted by Codin
+ <[email protected]> and sponsored/reviewed by Bastian Germann
+ <[email protected]> in Debian bug #1142925. This revision adds DEP-3
+ metadata and, where missing from that debdiff, folds in the matching
+ upstream Cassandane regression test(s).
+ .
+ This is CYR-2868, CVE-2026-47086.
+ .
+ You can't grant access to things you can't access!  Without this check,
+ any authenticated user could mint a URLAUTH token for any mailbox they
+ could name, because cmd_urlfetch skipped the ACL check whenever
+ urlauth.access is set -- it trusts that the presence of a valid HMAC
+ means the authorizing user was entitled to delegate.
+ .
+ Require the authorizer to have at least ACL_READ on the mailbox before
+ issuing a token.
+ .
+ This problem reported by Matthew Horsfall.
+ .
+ Co-Authored-By: Claude <[email protected]>
+ .
+ Cherry-picked from upstream commits:
+   88d9bf6e7e cassandane: test for ACL bypass in URLAuth
+   e7a28ccf9b imapd: require read access on mailbox in GENURLAUTH
+Author: Ricardo Signes <[email protected]>
+Origin: upstream, https://github.com/cyrusimap/cyrus-imapd/commit/e7a28ccf9b
+Bug: https://security-tracker.debian.org/tracker/CVE-2026-47086
+Bug-Debian: https://bugs.debian.org/1142925
+Bug-CVE: CVE-2026-47086
+Forwarded: not-needed
+Last-Update: 2026-07-25
+
+Index: cyrus-imapd/cassandane/Cassandane/Cyrus/URLAuth.pm
+===================================================================
+--- cyrus-imapd.orig/cassandane/Cassandane/Cyrus/URLAuth.pm
++++ cyrus-imapd/cassandane/Cassandane/Cyrus/URLAuth.pm
+@@ -272,4 +272,50 @@ sub test_revoked_access_invalidates_url
+     $self->assert_null($stolen, 'URL must not fetch after authorizer access revoked');
+ }
+ 
++sub test_perms
++    :UnixHierarchySep :AltNamespace
++{
++    my ($self) = @_;
++
++    $self->{instance}->create_user('other');
++
++  my $msg = $self->make_message("Message A");
++
++  my $url;
++  my %handlers = (
++    genurlauth => sub {
++      my ($cmd, $params) = @_;
++      $url = $params->[0];
++    }
++  );
++
++    my $svc = $self->{instance}->get_service('imap');
++    my $store = $svc->create_store(username => 'other');
++    my $talk = $store->get_client();
++
++  $talk->_imap_cmd(
++    'genurlauth', 0, \%handlers,
++    "imap://other\@127.0.0.1/Other%20Users%2fcassandane/;uid=1;urlauth=user+other",
++    "INTERNAL",
++  );
++
++  # This should fail in some way, commented out to show we really do fetch the
++  # email
++#  $self->assert_null($url);
++
++  my $email;
++  %handlers = (
++    urlfetch => sub {
++      my ($cmd, $params) = @_;
++      $email = ${$params}[1];
++    },
++  );
++
++  my $res = $talk->_imap_cmd('urlfetch', 0, \%handlers, $url);
++
++  warn $email if $email;
++
++  $self->assert_null($email);
++}
++
+ 1;
+Index: cyrus-imapd/imap/imapd.c
+===================================================================
+--- cyrus-imapd.orig/imap/imapd.c
++++ cyrus-imapd/imap/imapd.c
+@@ -14261,6 +14261,14 @@ static void cmd_genurlauth(char *tag)
+             continue;
+         }
+ 
++        /* You can't give permission you don't have! */
++        if (!imapd_userisadmin &&
++            !(cyrus_acl_myrights(imapd_authstate, mbentry->acl) & ACL_READ)) {
++            mboxlist_entry_free(&mbentry);
++            r = IMAP_BADURL;
++            goto err;
++        }
++
+         mboxlist_entry_free(&mbentry);
+ 
+         /* lookup key */
diff -Nru cyrus-imapd-3.10.2/debian/patches/0028-imapd-fix-cmd_delete-force-handling.patch cyrus-imapd-3.10.2/debian/patches/0028-imapd-fix-cmd_delete-force-handling.patch
--- cyrus-imapd-3.10.2/debian/patches/0028-imapd-fix-cmd_delete-force-handling.patch	1970-01-01 02:00:00.000000000 +0200
+++ cyrus-imapd-3.10.2/debian/patches/0028-imapd-fix-cmd_delete-force-handling.patch	2026-08-02 12:00:00.000000000 +0200
@@ -0,0 +1,116 @@
+Description: imapd: fix cmd_delete 'force' handling (LOCALDELETE ACL bypass) (CVE-2026-47084)
+ An initial debdiff for this CVE batch was submitted by Codin
+ <[email protected]> and sponsored/reviewed by Bastian Germann
+ <[email protected]> in Debian bug #1142925. This revision adds DEP-3
+ metadata and, where missing from that debdiff, folds in the matching
+ upstream Cassandane regression test(s).
+ .
+ This is CYR-2867, CVE-2026-47084.
+ .
+ Non-admin users could invoke the admin-only LOCALDELETE command
+ and delete mailboxes without permission, because MBOXLIST_DELETE_FORCE
+ wasn't being plumbed through to mboxlist_deletemailbox.
+ .
+ Reported by Michael Lynch (mtlynch.io).
+ .
+ Cherry-picked from upstream commits:
+   2c871deac5 Delete: test localdelete of other user's mailbox
+   304255f5be imapd: fix cmd_delete 'force' handling
+   7dfc43c681 MurderIMAP: localdelete requires admin privs
+Author: ellie timoney <[email protected]>
+Origin: upstream, https://github.com/cyrusimap/cyrus-imapd/commit/304255f5be
+Bug: https://security-tracker.debian.org/tracker/CVE-2026-47084
+Bug-Debian: https://bugs.debian.org/1142925
+Bug-CVE: CVE-2026-47084
+Forwarded: not-needed
+Last-Update: 2026-07-25
+
+diff --git a/cassandane/Cassandane/Cyrus/Delete.pm b/cassandane/Cassandane/Cyrus/Delete.pm
+index 10f9fb6..fc778af 100644
+--- a/cassandane/Cassandane/Cyrus/Delete.pm
++++ b/cassandane/Cassandane/Cyrus/Delete.pm
+@@ -1085,4 +1085,30 @@ sub test_cyr_expire_delete_noexpire
+     $self->check_syslog($self->{instance});
+ }
+ 
++sub test_localdelete_other
++    :UnixHierarchySep :AltNamespace
++{
++    my ($self) = @_;
++
++    $self->{instance}->create_user("other");
++
++    my $admintalk = $self->{adminstore}->get_client();
++    $admintalk->create('user/other/foo');
++    $self->assert_str_equals('ok', $admintalk->get_last_completion_response());
++    $admintalk->setacl('user/other/foo', 'cassandane' => 'l');
++    $self->assert_str_equals('ok', $admintalk->get_last_completion_response());
++
++    my $imaptalk = $self->{store}->get_client();
++
++    # shouldn't be able to delete another user's mailbox
++    $imaptalk->delete('Other Users/other/foo');
++    $self->assert_str_equals('no', $imaptalk->get_last_completion_response());
++    $self->assert_matches(qr{Permission denied}, $imaptalk->get_last_error());
++
++    # shouldn't be able to localdelete it either!
++    $imaptalk->localdelete('Other Users/other/foo');
++    $self->assert_str_equals('no', $imaptalk->get_last_completion_response());
++    $self->assert_matches(qr{Permission denied}, $imaptalk->get_last_error());
++}
++
+ 1;
+diff --git a/cassandane/Cassandane/Cyrus/MurderIMAP.pm b/cassandane/Cassandane/Cyrus/MurderIMAP.pm
+index fbf631b..0a83897 100644
+--- a/cassandane/Cassandane/Cyrus/MurderIMAP.pm
++++ b/cassandane/Cassandane/Cyrus/MurderIMAP.pm
+@@ -294,14 +294,18 @@ sub test_move_to_backend_nonexistent
+ 
+     my $frontend = $self->{frontend_store}->get_client();
+     my $backend = $self->{backend1_store}->get_client();
++    my $backend_admin = $self->{backend1_adminstore}->get_client();
+ 
+     # create a destination folder (on both frontend and backend)
+     $frontend->create($dest_folder);
+     $self->assert_str_equals('ok', $frontend->get_last_completion_response());
+ 
+     # nuke the destination folder (on the backend only)
+-    $backend->localdelete($dest_folder);
+-    $self->assert_str_equals('ok', $backend->get_last_completion_response());
++    # n.b. 'INBOX' is part of the mailbox name, it's not just the cassandane
++    # user's inbox, hence the weird admin path here.  Not sure why I did it
++    # that way, might have been just what was in the bug report.
++    $backend_admin->localdelete('user.cassandane.INBOX.dest');
++    $self->assert_str_equals('ok', $backend_admin->get_last_completion_response());
+ 
+     my $f_folders = $frontend->list('', '*');
+     $self->assert_deep_equals(
+diff --git a/imap/imapd.c b/imap/imapd.c
+index 2cb5126..51aecc7 100644
+--- a/imap/imapd.c
++++ b/imap/imapd.c
+@@ -7346,19 +7346,22 @@ static void cmd_delete(char *tag, char *name, int localonly, int force)
+     /* local mailbox */
+     if (!r) {
+         int isadmin = imapd_userisadmin || imapd_userisproxyadmin;
++        int delflags = force ? MBOXLIST_DELETE_FORCE : 0;
+ 
+         if (mbname_isdeleted(mbname)) {
+             r = mboxlist_deletemailbox(mbname_intname(mbname),
+                                        isadmin, imapd_userid,
+                                        imapd_authstate, mboxevent,
+-                                       MBOXLIST_DELETE_LOCALONLY);
++                                       delflags | MBOXLIST_DELETE_LOCALONLY);
+         }
+         else if (!isadmin && mbname_issystem(mbname)) {
+             r = IMAP_PERMISSION_DENIED;
+         }
+         else {
+             delete_user = mboxname_isusermailbox(mbname_intname(mbname), 1);
+-            int delflags = (1-force) ? MBOXLIST_DELETE_CHECKACL : 0;
++
++            if (!force)
++                delflags |= MBOXLIST_DELETE_CHECKACL;
+ 
+             if (!delete_user && mboxlist_haschildren(mbname_intname(mbname))) {
+                 r = IMAP_MAILBOX_HASCHILDREN;
diff -Nru cyrus-imapd-3.10.2/debian/patches/series cyrus-imapd-3.10.2/debian/patches/series
--- cyrus-imapd-3.10.2/debian/patches/series	2026-07-12 21:29:05.000000000 +0200
+++ cyrus-imapd-3.10.2/debian/patches/series	2026-08-02 12:00:00.000000000 +0200
@@ -8,3 +8,12 @@
 0018-increase-test-timeout.patch
 #0019-propagate-XXFLAGS.patch
 eventsource-without-websocket.patch
+0020-lmtp_sieve.c-enforce-ACL-on-vacation-fcc-destination.patch
+0021-imap-message.c-fix-heap-exposure-in-nested-MIME-comm.patch
+0022-imapd-MULTISEARCH-must-check-ACL_READ-on-each-mailbo.patch
+0023-imapd-reject-URLFETCH-when-no-mboxkey-exists-for-the.patch
+0024-imapd-LISTRIGHTS-requires-admin-rights.patch
+0025-imapd-require-read-access-for-XAPPLEPUSHSERVICE-mail.patch
+0026-imapd.c-re-check-URLAUTH-authorizer-access-just-in-t.patch
+0027-imapd-require-read-access-on-mailbox-in-GENURLAUTH.patch
+0028-imapd-fix-cmd_delete-force-handling.patch
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.