Package: release.debian.org
Severity: normal
User: release.debian....@packages.debian.org
Usertags: unblock

Please unblock package dovecot

[ Reason ]

Dovecot 1:2.3.13+dfsg1-2 includes two targeted security fixes and no other
changes.  The relevant section of debian/changelog is:

dovecot (1:2.3.13+dfsg1-2) unstable; urgency=high

  * Import upstream fixes for security issues (Closes: #990566):
    - CVE-2021-29157: Path traversal issue allowing an attacker with
      access to the local filesystem can trick OAuth2 authentication into
      using an HS256 validation key from an attacker-controlled location
    - CVE-2021-33515: Sensitive information could be redirected to an
      attacker-controlled address because of a STARTTLS command injection
      bug in the submission service

 -- Noah Meyerhans <no...@debian.org>  Tue, 20 Jul 2021 08:05:19 -0700

[ Impact ]

We release bullseye with known security issues and will likely need to fix them
in a subsequent point release.

[ Tests ]

I've done basic functionality testing.  Additionally, the fixes are backported
from upstream's changes and are already included in Ubuntu's security archive,
so they've gotten reasonable test coverage at that level.  I do not have
reproducers for the security issues, so I have been unable to verify
experimentally that the problems have been fixed.

[ Risks ]

Code changes are reasonably straightforward, and as mentioned are already
deployed elsewhere.  Further, they impact what I believe to be less common
features of dovecot, meaning that even in the worst-case scenario, impact is
likely to be limited to a small number of users.

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

[ Other info ]

n/a

unblock dovecot/1:2.3.13+dfsg1-2
diff -Nru dovecot-2.3.13+dfsg1/debian/changelog 
dovecot-2.3.13+dfsg1/debian/changelog
--- dovecot-2.3.13+dfsg1/debian/changelog       2021-01-25 23:38:17.000000000 
+0000
+++ dovecot-2.3.13+dfsg1/debian/changelog       2021-07-20 15:05:19.000000000 
+0000
@@ -1,3 +1,15 @@
+dovecot (1:2.3.13+dfsg1-2) unstable; urgency=high
+
+  * Import upstream fixes for security issues (Closes: #990566):
+    - CVE-2021-29157: Path traversal issue allowing an attacker with
+      access to the local filesystem can trick OAuth2 authentication into
+      using an HS256 validation key from an attacker-controlled location
+    - CVE-2021-33515: Sensitive information could be redirected to an
+      attacker-controlled address because of a STARTTLS command injection
+      bug in the submission service
+
+ -- Noah Meyerhans <no...@debian.org>  Tue, 20 Jul 2021 08:05:19 -0700
+
 dovecot (1:2.3.13+dfsg1-1) unstable; urgency=medium
 
   [ Christian Göttsche ]
diff -Nru dovecot-2.3.13+dfsg1/debian/patches/CVE-2021-29157.patch 
dovecot-2.3.13+dfsg1/debian/patches/CVE-2021-29157.patch
--- dovecot-2.3.13+dfsg1/debian/patches/CVE-2021-29157.patch    1970-01-01 
00:00:00.000000000 +0000
+++ dovecot-2.3.13+dfsg1/debian/patches/CVE-2021-29157.patch    2021-07-19 
22:09:38.000000000 +0000
@@ -0,0 +1,134 @@
+Description: fix incorrectly escapes kid and azp fields in JWT tokens
+Origin: 
https://launchpadlibrarian.net/544092180/dovecot_1%3A2.3.13+dfsg1-1ubuntu1_1%3A2.3.13+dfsg1-1ubuntu1.1.diff.gz
+
+--- a/src/lib-dict-extra/dict-fs.c
++++ b/src/lib-dict-extra/dict-fs.c
+@@ -67,8 +67,37 @@ static void fs_dict_deinit(struct dict *
+       i_free(dict);
+ }
+ 
++/* Remove unsafe paths */
++static const char *fs_dict_escape_key(const char *key)
++{
++      const char *ptr;
++      string_t *new_key = NULL;
++      /* we take the slow path always if we see potential
++         need for escaping */
++      while ((ptr = strstr(key, "/.")) != NULL) {
++              /* move to the first dot */
++              const char *ptr2 = ptr + 1;
++              /* find position of non-dot */
++              while (*ptr2 == '.') ptr2++;
++              if (new_key == NULL)
++                      new_key = t_str_new(strlen(key));
++              str_append_data(new_key, key, ptr - key);
++              /* if ptr2 is / or end of string, escape */
++              if (*ptr2 == '/' || *ptr2 == '\0')
++                      str_append(new_key, "/...");
++              else
++                      str_append(new_key, "/.");
++              key = ptr + 2;
++      }
++      if (new_key == NULL)
++              return key;
++      str_append(new_key, key);
++      return str_c(new_key);
++}
++
+ static const char *fs_dict_get_full_key(struct fs_dict *dict, const char *key)
+ {
++      key = fs_dict_escape_key(key);
+       if (str_begins(key, DICT_PATH_SHARED))
+               return key + strlen(DICT_PATH_SHARED);
+       else if (str_begins(key, DICT_PATH_PRIVATE)) {
+--- a/src/lib-oauth2/oauth2-jwt.c
++++ b/src/lib-oauth2/oauth2-jwt.c
+@@ -250,6 +250,34 @@ oauth2_jwt_copy_fields(ARRAY_TYPE(oauth2
+       }
+ }
+ 
++/* Escapes '/' and '%' in identifier to %hex */
++static const char *escape_identifier(const char *identifier)
++{
++      size_t pos = strcspn(identifier, "/%");
++      /* nothing to escape */
++      if (identifier[pos] == '\0')
++              return identifier;
++
++      size_t len = strlen(identifier);
++      string_t *new_id = t_str_new(len);
++      str_append_data(new_id, identifier, pos);
++
++      for (size_t i = pos; i < len; i++) {
++              switch (identifier[i]) {
++              case '/':
++                      str_append(new_id, "%2f");
++                      break;
++              case '%':
++                      str_append(new_id, "%25");
++                      break;
++              default:
++                      str_append_c(new_id, identifier[i]);
++                      break;
++              }
++      }
++      return str_c(new_id);
++}
++
+ static int
+ oauth2_jwt_header_process(struct json_tree *tree, const char **alg_r,
+                         const char **kid_r, const char **error_r)
+@@ -349,6 +377,8 @@ oauth2_jwt_body_process(const struct oau
+       const char *azp = get_field(tree, "azp");
+       if (azp == NULL)
+               azp = "default";
++      else
++              azp = escape_identifier(azp);
+ 
+       if (oauth2_validate_signature(set, azp, alg, kid, blobs, error_r) < 0)
+               return -1;
+@@ -401,31 +431,8 @@ int oauth2_try_parse_jwt(const struct oa
+       else if (*kid == '\0') {
+               *error_r = "'kid' field is empty";
+               return -1;
+-      }
+-
+-      size_t pos = strcspn(kid, "./%");
+-      if (pos < strlen(kid)) {
+-              /* sanitize kid, cannot allow dots or / in it, so we encode 
them */
+-              string_t *new_kid = t_str_new(strlen(kid));
+-              /* put initial data */
+-              str_append_data(new_kid, kid, pos);
+-              for (const char *c = kid+pos; *c != '\0'; c++) {
+-                      switch (*c) {
+-                      case '.':
+-                              str_append(new_kid, "%2e");
+-                              break;
+-                      case '/':
+-                              str_append(new_kid, "%2f");
+-                              break;
+-                      case '%':
+-                              str_append(new_kid, "%25");
+-                              break;
+-                      default:
+-                              str_append_c(new_kid, *c);
+-                              break;
+-                      }
+-              }
+-              kid = str_c(new_kid);
++      } else {
++              kid = escape_identifier(kid);
+       }
+ 
+       /* parse body */
+--- a/src/lib-oauth2/test-oauth2-jwt.c
++++ b/src/lib-oauth2/test-oauth2-jwt.c
+@@ -508,7 +508,7 @@ static void test_jwt_kid_escape(void)
+        void *ptr = buffer_append_space_unsafe(secret, 32);
+        random_fill(ptr, 32);
+        buffer_t *b64_key = t_base64_encode(0, SIZE_MAX, secret->data, 
secret->used);
+-       save_key_to("HS256", "hello%2eworld%2f%25", str_c(b64_key));
++       save_key_to("HS256", "hello.world%2f%25", str_c(b64_key));
+       /* make a token */
+       buffer_t *tokenbuf = create_jwt_token_kid("HS256", "hello.world/%");
+       /* sign it */
diff -Nru dovecot-2.3.13+dfsg1/debian/patches/CVE-2021-33515.patch 
dovecot-2.3.13+dfsg1/debian/patches/CVE-2021-33515.patch
--- dovecot-2.3.13+dfsg1/debian/patches/CVE-2021-33515.patch    1970-01-01 
00:00:00.000000000 +0000
+++ dovecot-2.3.13+dfsg1/debian/patches/CVE-2021-33515.patch    2021-07-19 
22:05:12.000000000 +0000
@@ -0,0 +1,62 @@
+commit 321c339756f9b2b98fb7326359d1333adebb5295
+Author: Stephan Bosch <stephan.bo...@open-xchange.com>
+Date:   Sat May 22 00:16:38 2021 +0200
+
+    lib-smtp: smtp-server-connection - Fix STARTTLS command injection 
vulnerability.
+    
+    The input handler kept reading more commands even though the input was 
locked by
+    the STARTTLS command, thereby causing it to read the command pipelined 
beyond
+    STARTTLS. This causes a STARTTLS command injection vulerability.
+
+--- a/src/lib-smtp/smtp-server-cmd-starttls.c
++++ b/src/lib-smtp/smtp-server-cmd-starttls.c
+@@ -37,6 +37,13 @@ static int cmd_starttls_start(struct smt
+               return -1;
+       }
+ 
++      /* The command queue must be empty at this point. If anything were to be
++         queued somehow, this connection is vulnerable to STARTTLS command
++         insertion.
++       */
++      i_assert(conn->command_queue_count == 0 &&
++               conn->command_queue_head == NULL);
++
+       /* RFC 3207, Section 4.2:
+ 
+          Upon completion of the TLS handshake, the SMTP protocol is reset to
+@@ -107,6 +114,13 @@ cmd_starttls_next(struct smtp_server_cmd
+       const struct smtp_server_callbacks *callbacks = conn->callbacks;
+       int ret;
+ 
++      /* The command queue can only contain the STARTTLS command at this
++         point. If anything beyond the STARTTLS were queued somehow, this
++         connection is vulnerable to STARTTLS command insertion.
++       */
++      i_assert(conn->command_queue_count == 1 &&
++               conn->command_queue_tail == command);
++
+       smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_STARTTLS,
+                                        NULL);
+ 
+--- a/src/lib-smtp/smtp-server-connection.c
++++ b/src/lib-smtp/smtp-server-connection.c
+@@ -440,7 +440,7 @@ smtp_server_connection_handle_input(stru
+ 
+       /* Parse commands */
+       ret = 1;
+-      while (!conn->closing && ret != 0) {
++      while (!conn->closing && !conn->input_locked && ret != 0) {
+               while ((ret = smtp_command_parse_next(
+                       conn->smtp_parser, &cmd_name, &cmd_params,
+                       &error_code, &error)) > 0) {
+@@ -464,6 +464,10 @@ smtp_server_connection_handle_input(stru
+ 
+                       if (conn->disconnected)
+                               return;
++                      /* Last command locked the input; stop trying to read
++                         more. */
++                      if (conn->input_locked)
++                              break;
+                       /* Client indicated it will close after this command;
+                          stop trying to read more. */
+                       if (conn->closing)
diff -Nru dovecot-2.3.13+dfsg1/debian/patches/series 
dovecot-2.3.13+dfsg1/debian/patches/series
--- dovecot-2.3.13+dfsg1/debian/patches/series  2021-01-25 23:38:17.000000000 
+0000
+++ dovecot-2.3.13+dfsg1/debian/patches/series  2021-07-19 22:05:12.000000000 
+0000
@@ -16,3 +16,5 @@
 Fix-32bit-sign-comparisons.patch
 Fix-32-bit-test-case.patch
 Improve-cross-compile-support.patch
+CVE-2021-29157.patch
+CVE-2021-33515.patch

Reply via email to