Bug#1013944: bullseye-pu: package cyrus-imapd/3.2.6-2+deb11u2
Package: release.debian.org
Severity: normal
Tags: bullseye
User: release.debian.org@packages.debian.org
Usertags: pu
[ Reason ]
Bookworm will provide cyrus-imapd 3.6.x. To permit a safe upgrade from
3.2.6, updtream provided a patch for versions 3.4 and 3.2. It ensure
that mailboxes have an unique id.
[ Impact ]
Risk during Bullseye to Bookworm upgrade.
[ Tests ]
Test passed
(https://salsa.debian.org/debian/cyrus-imapd/-/pipelines/393112)
[ Risks ]
This patch is the difference between 3.2.9 and 3.2.10, applied without
any change.
[ Checklist ]
[X] *all* changes are documented in the d/changelog
[X] I reviewed all changes and I approve them
[X] attach debdiff against the package in (old)stable
[X] the issue is verified as fixed in unstable
[ Changes ]
Cyrus tools now check if mailbox id is really unique.
Cheers,
Yadd
diff --git a/debian/changelog b/debian/changelog
index ca4d2a92..209a040f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+cyrus-imapd (3.2.6-2+deb11u2) bullseye; urgency=medium
+
+ * Ensure that ctl_cyrusdb -r and reconstruct now ensure the "uniqueid" field
+ is present in and synchronised between mailboxes.db and cyrus.header.
+ Required before 3.6.x upgrade
+
+ -- Yadd <yadd@debian.org> Mon, 27 Jun 2022 21:41:17 +0200
+
cyrus-imapd (3.2.6-2+deb11u1) bullseye; urgency=high
* Replace string hashing algorithm (Closes: #993433, CVE-2021-33582)
diff --git a/debian/patches/prepare-3.6-upgrade.patch b/debian/patches/prepare-3.6-upgrade.patch
new file mode 100644
index 00000000..a7b8aea0
--- /dev/null
+++ b/debian/patches/prepare-3.6-upgrade.patch
@@ -0,0 +1,244 @@
+Description: reconstruct mailboxes to prepare
+ ctl_cyrusdb -r and reconstruct now ensure the "uniqueid" field is present
+ in and synchronised between mailboxes.db and cyrus.header.
+Author: ellie timoney <ellie@fastmail.com>
+Origin: upstream, https://github.com/cyrusimap/cyrus-imapd/commit/360e5d153
+ https://github.com/cyrusimap/cyrus-imapd/commit/93b01dd83
+ https://github.com/cyrusimap/cyrus-imapd/commit/0f59f9f36
+ https://github.com/cyrusimap/cyrus-imapd/commit/0ee7d128a
+ https://github.com/cyrusimap/cyrus-imapd/commit/2918ce8f0
+ https://github.com/cyrusimap/cyrus-imapd/commit/a330b471f
+ https://github.com/cyrusimap/cyrus-imapd/commit/df58b26cb
+Bug: https://github.com/cyrusimap/cyrus-imapd/pull/4100
+Forwarded: not-needed
+Reviewed-By: Yadd <yadd@debian.org>
+Last-Update: 2022-06-27
+
+--- a/imap/ctl_cyrusdb.c
++++ b/imap/ctl_cyrusdb.c
+@@ -129,7 +129,7 @@
+ static int fixmbox(const mbentry_t *mbentry,
+ void *rock __attribute__((unused)))
+ {
+- int r;
++ int r, r2;
+
+ /* if MBTYPE_RESERVED, unset it & call mboxlist_delete */
+ if (mbentry->mbtype & MBTYPE_RESERVE) {
+@@ -172,12 +172,66 @@
+ mbentry->name, cyrusdb_strerror(r));
+ }
+
++ /* make sure every local mbentry has a uniqueid! */
++ if (!mbentry->uniqueid && mbentry_is_local_mailbox(mbentry)) {
++ struct mailbox *mailbox = NULL;
++ struct mboxlock *namespacelock = NULL;
++ mbentry_t *copy = NULL;
++
++ r = mailbox_open_iwl(mbentry->name, &mailbox);
++ if (r) {
++ /* XXX what does it mean if there's an mbentry, but the mailbox
++ * XXX was not openable?
++ */
++ syslog(LOG_DEBUG, "%s: mailbox_open_iwl %s returned %s",
++ __func__, mbentry->name, error_message(r));
++ goto skip_uniqueid;
++ }
++
++ if (!mailbox->uniqueid) {
++ /* yikes, no uniqueid in header either! */
++ mailbox_make_uniqueid(mailbox);
++ syslog(LOG_INFO, "mailbox %s header had no uniqueid, creating %s",
++ mbentry->name, mailbox->uniqueid);
++ }
++
++ copy = mboxlist_entry_copy(mbentry);
++ copy->uniqueid = xstrdup(mailbox->uniqueid);
++ syslog(LOG_INFO, "mbentry %s had no uniqueid, setting %s from header",
++ copy->name, copy->uniqueid);
++
++ namespacelock = mboxname_usernamespacelock(copy->name);
++ r = mboxlist_update(copy, /*localonly*/1);
++ mboxname_release(&namespacelock);
++ if (r) {
++ syslog(LOG_ERR, "failed to update mboxlist for %s: %s",
++ mbentry->name, error_message(r));
++ r2 = mailbox_abort(mailbox);
++ if (r2) {
++ syslog(LOG_ERR, "DBERROR: error aborting transaction: %s",
++ cyrusdb_strerror(r2));
++ }
++ }
++ else {
++ r2 = mailbox_commit(mailbox);
++ if (r2) {
++ syslog(LOG_ERR, "DBERROR: error committing transaction: %s",
++ cyrusdb_strerror(r2));
++ }
++ }
++ mailbox_close(&mailbox);
++ mboxlist_entry_free(©);
++
++skip_uniqueid:
++ ; /* hush "label at end of compound statement" warning */
++ }
++
+ return 0;
+ }
+
+ static void process_mboxlist(void)
+ {
+- /* build a list of mailboxes - we're using internal names here */
++ /* run fixmbox across all mboxlist entries */
+ mboxlist_allmbox(NULL, fixmbox, NULL, MBOXTREE_INTERMEDIATES);
+
+ /* enable or disable RACLs per config */
+--- a/imap/mailbox.c
++++ b/imap/mailbox.c
+@@ -995,6 +995,12 @@
+ goto done;
+ }
+
++ /* XXX can we even get here for remote mbentries? */
++ if (!mbentry->uniqueid && mbentry_is_local_mailbox(mbentry)) {
++ syslog(LOG_NOTICE, "mbentry %s has no uniqueid, needs reconstruct",
++ mailbox->name);
++ }
++
+ mailbox->part = xstrdup(mbentry->partition);
+
+ /* Note that the header does have the ACL information, but it is only
+@@ -1288,7 +1294,11 @@
+ if (!tab || tab > eol) tab = eol;
+ mailbox->uniqueid = xstrndup(p, tab - p);
+ }
+- /* else, uniqueid needs to be generated when we know the uidvalidity */
++ else {
++ /* ancient cyrus.header file without a uniqueid field! */
++ syslog(LOG_NOTICE, "mailbox %s header has no uniqueid, needs reconstruct",
++ mailbox->name);
++ }
+
+ /* Read names of user flags */
+ p = eol + 1;
+@@ -6148,6 +6158,9 @@
+ mailbox_set_uniqueid(mailbox, mbentry->uniqueid);
+ }
+ else {
++ if (!mailbox->uniqueid) {
++ mailbox_make_uniqueid(mailbox);
++ }
+ free(mbentry->uniqueid);
+ mbentry->uniqueid = xstrdup(mailbox->uniqueid);
+ r = mboxlist_update(mbentry, 0);
+--- a/imap/mboxlist.c
++++ b/imap/mboxlist.c
+@@ -271,6 +271,26 @@
+ uid);
+ }
+
++EXPORTED int mbentry_is_local_mailbox(const struct mboxlist_entry *mbentry)
++{
++ if (config_mupdate_server && !config_getstring(IMAPOPT_PROXYSERVERS)) {
++ /* dedicated frontends never have local mailboxes */
++ return 0;
++ }
++ else if ((mbentry->mbtype & MBTYPE_REMOTE)) {
++ /* mbentry has the remote flag set */
++ return 0;
++ }
++ else if (mbentry->server
++ && 0 != strcmpsafe(mbentry->server, config_servername))
++ {
++ /* it's on some server that is not this one */
++ return 0;
++ }
++
++ return 1;
++}
++
+ /*
+ * read a single record from the mailboxes.db and return a pointer to it
+ */
+--- a/imap/mboxlist.h
++++ b/imap/mboxlist.h
+@@ -123,6 +123,9 @@
+ char *mbentry_metapath(const struct mboxlist_entry *mbentry, int metatype, int isnew);
+ char *mbentry_datapath(const struct mboxlist_entry *mbentry, uint32_t);
+
++int mbentry_is_local_mailbox(const struct mboxlist_entry *mbentry);
++#define mbentry_is_remote_mailbox(mbentry) (!mbentry_is_local_mailbox(mbentry))
++
+ int mboxlist_parse_entry(mbentry_t **mbentryptr,
+ const char *name, size_t namelen,
+ const char *data, size_t datalen);
+--- a/imap/reconstruct.c
++++ b/imap/reconstruct.c
+@@ -468,33 +468,45 @@
+ return 0;
+ }
+
+- other = hash_lookup(mailbox->uniqueid, &unqid_table);
+- if (other) {
+- mbentry_t *oldmbentry = NULL;
+- /* check that the old one still exists! */
+- r = mboxlist_lookup(other, &oldmbentry, NULL);
+- if (!r && !strcmpsafe(oldmbentry->uniqueid, mailbox->uniqueid)) {
+- /* uniqueid change required! */
+- if (updateuniqueids) {
+- mailbox_make_uniqueid(mailbox);
+- syslog (LOG_ERR, "uniqueid clash with %s - changed %s (%s => %s)",
+- other, mailbox->name, oldmbentry->uniqueid, mailbox->uniqueid);
+- }
+- else {
+- syslog (LOG_ERR, "uniqueid clash with %s for %s (%s)",
+- other, mailbox->name, mailbox->uniqueid);
+- }
+- }
+- mboxlist_entry_free(&oldmbentry);
+- }
+-
+- hash_insert(mailbox->uniqueid, xstrdup(mailbox->name), &unqid_table);
+-
+ /* Convert internal name to external */
+ char *extname = mboxname_to_external(name, &recon_namespace, NULL);
+ if (!(reconstruct_flags & RECONSTRUCT_QUIET))
+ printf("%s\n", extname);
+
++ if (mailbox->uniqueid) {
++ other = hash_lookup(mailbox->uniqueid, &unqid_table);
++ if (other) {
++ mbentry_t *oldmbentry = NULL;
++ /* check that the old one still exists! */
++ r = mboxlist_lookup(other, &oldmbentry, NULL);
++ if (!r && !strcmpsafe(oldmbentry->uniqueid, mailbox->uniqueid)) {
++ /* uniqueid change required! */
++ if (updateuniqueids) {
++ mailbox_make_uniqueid(mailbox);
++ syslog (LOG_ERR, "uniqueid clash with %s - changed %s (%s => %s)",
++ other, mailbox->name, oldmbentry->uniqueid, mailbox->uniqueid);
++ }
++ else {
++ syslog (LOG_ERR, "uniqueid clash with %s for %s (%s)",
++ other, mailbox->name, mailbox->uniqueid);
++ }
++ }
++ mboxlist_entry_free(&oldmbentry);
++ }
++
++ hash_insert(mailbox->uniqueid, xstrdup(mailbox->name), &unqid_table);
++ }
++ else {
++ /* We should only get here for -V (setversion) or -n (no changes)
++ * modes. Otherwise, mailbox_reconstruct() should have dealt with it
++ * already.
++ * It would be nice if -V would also ensure there's a uniqueid, but
++ * that change would require a refactor that's already on 3.6 but too
++ * intrusive to backport.
++ */
++ printf("%s has no uniqueid, needs real reconstruct\n", extname);
++ }
++
+ strncpy(outpath, mailbox_meta_fname(mailbox, META_HEADER), MAX_MAILBOX_NAME);
+
+ if (setversion && setversion != mailbox->i.minor_version) {
diff --git a/debian/patches/series b/debian/patches/series
index 1577d428..60373820 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -9,3 +9,4 @@
0018-increase-test-timeout.patch
CVE-2021-32056.patch
CVE-2021-33582.patch
+prepare-3.6-upgrade.patch
Reply to: