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
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

Reply via email to