Blame SOURCES/kvm-nbd-server-Avoid-long-error-message-assertions-CVE-2.patch

77c23f
From f49ff2ed5675f1d0cddc404842e9d6e4e572d5a7 Mon Sep 17 00:00:00 2001
77c23f
From: Eric Blake <eblake@redhat.com>
77c23f
Date: Wed, 10 Jun 2020 18:32:01 -0400
77c23f
Subject: [PATCH 1/2] nbd/server: Avoid long error message assertions
77c23f
 CVE-2020-10761
77c23f
77c23f
RH-Author: Eric Blake <eblake@redhat.com>
77c23f
Message-id: <20200610183202.3780750-2-eblake@redhat.com>
77c23f
Patchwork-id: 97494
77c23f
O-Subject: [RHEL-AV-8.2.1 qemu-kvm PATCH 1/2] nbd/server: Avoid long error message assertions CVE-2020-10761
77c23f
Bugzilla: 1845384
77c23f
RH-Acked-by: Sergio Lopez Pascual <slp@redhat.com>
77c23f
RH-Acked-by: Max Reitz <mreitz@redhat.com>
77c23f
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
77c23f
77c23f
Ever since commit 36683283 (v2.8), the server code asserts that error
77c23f
strings sent to the client are well-formed per the protocol by not
77c23f
exceeding the maximum string length of 4096.  At the time the server
77c23f
first started sending error messages, the assertion could not be
77c23f
triggered, because messages were completely under our control.
77c23f
However, over the years, we have added latent scenarios where a client
77c23f
could trigger the server to attempt an error message that would
77c23f
include the client's information if it passed other checks first:
77c23f
77c23f
- requesting NBD_OPT_INFO/GO on an export name that is not present
77c23f
  (commit 0cfae925 in v2.12 echoes the name)
77c23f
77c23f
- requesting NBD_OPT_LIST/SET_META_CONTEXT on an export name that is
77c23f
  not present (commit e7b1948d in v2.12 echoes the name)
77c23f
77c23f
At the time, those were still safe because we flagged names larger
77c23f
than 256 bytes with a different message; but that changed in commit
77c23f
93676c88 (v4.2) when we raised the name limit to 4096 to match the NBD
77c23f
string limit.  (That commit also failed to change the magic number
77c23f
4096 in nbd_negotiate_send_rep_err to the just-introduced named
77c23f
constant.)  So with that commit, long client names appended to server
77c23f
text can now trigger the assertion, and thus be used as a denial of
77c23f
service attack against a server.  As a mitigating factor, if the
77c23f
server requires TLS, the client cannot trigger the problematic paths
77c23f
unless it first supplies TLS credentials, and such trusted clients are
77c23f
less likely to try to intentionally crash the server.
77c23f
77c23f
We may later want to further sanitize the user-supplied strings we
77c23f
place into our error messages, such as scrubbing out control
77c23f
characters, but that is less important to the CVE fix, so it can be a
77c23f
later patch to the new nbd_sanitize_name.
77c23f
77c23f
Consideration was given to changing the assertion in
77c23f
nbd_negotiate_send_rep_verr to instead merely log a server error and
77c23f
truncate the message, to avoid leaving a latent path that could
77c23f
trigger a future CVE DoS on any new error message.  However, this
77c23f
merely complicates the code for something that is already (correctly)
77c23f
flagging coding errors, and now that we are aware of the long message
77c23f
pitfall, we are less likely to introduce such errors in the future,
77c23f
which would make such error handling dead code.
77c23f
77c23f
Reported-by: Xueqiang Wei <xuwei@redhat.com>
77c23f
CC: qemu-stable@nongnu.org
77c23f
Fixes: https://bugzilla.redhat.com/1843684 CVE-2020-10761
77c23f
Fixes: 93676c88d7
77c23f
Signed-off-by: Eric Blake <eblake@redhat.com>
77c23f
Message-Id: <20200610163741.3745251-2-eblake@redhat.com>
77c23f
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
77c23f
(cherry picked from commit 5c4fe018c025740fef4a0a4421e8162db0c3eefd)
77c23f
Signed-off-by: Eric Blake <eblake@redhat.com>
77c23f
Signed-off-by: Eduardo Lima (Etrunko) <etrunko@redhat.com>
77c23f
---
77c23f
 nbd/server.c               | 23 ++++++++++++++++++++---
77c23f
 tests/qemu-iotests/143     |  4 ++++
77c23f
 tests/qemu-iotests/143.out |  2 ++
77c23f
 3 files changed, 26 insertions(+), 3 deletions(-)
77c23f
77c23f
diff --git a/nbd/server.c b/nbd/server.c
77c23f
index 24ebc1a805..d5b9df092c 100644
77c23f
--- a/nbd/server.c
77c23f
+++ b/nbd/server.c
77c23f
@@ -217,7 +217,7 @@ nbd_negotiate_send_rep_verr(NBDClient *client, uint32_t type,
77c23f
 
77c23f
     msg = g_strdup_vprintf(fmt, va);
77c23f
     len = strlen(msg);
77c23f
-    assert(len < 4096);
77c23f
+    assert(len < NBD_MAX_STRING_SIZE);
77c23f
     trace_nbd_negotiate_send_rep_err(msg);
77c23f
     ret = nbd_negotiate_send_rep_len(client, type, len, errp);
77c23f
     if (ret < 0) {
77c23f
@@ -231,6 +231,19 @@ nbd_negotiate_send_rep_verr(NBDClient *client, uint32_t type,
77c23f
     return 0;
77c23f
 }
77c23f
 
77c23f
+/*
77c23f
+ * Return a malloc'd copy of @name suitable for use in an error reply.
77c23f
+ */
77c23f
+static char *
77c23f
+nbd_sanitize_name(const char *name)
77c23f
+{
77c23f
+    if (strnlen(name, 80) < 80) {
77c23f
+        return g_strdup(name);
77c23f
+    }
77c23f
+    /* XXX Should we also try to sanitize any control characters? */
77c23f
+    return g_strdup_printf("%.80s...", name);
77c23f
+}
77c23f
+
77c23f
 /* Send an error reply.
77c23f
  * Return -errno on error, 0 on success. */
77c23f
 static int GCC_FMT_ATTR(4, 5)
77c23f
@@ -595,9 +608,11 @@ static int nbd_negotiate_handle_info(NBDClient *client, Error **errp)
77c23f
 
77c23f
     exp = nbd_export_find(name);
77c23f
     if (!exp) {
77c23f
+        g_autofree char *sane_name = nbd_sanitize_name(name);
77c23f
+
77c23f
         return nbd_negotiate_send_rep_err(client, NBD_REP_ERR_UNKNOWN,
77c23f
                                           errp, "export '%s' not present",
77c23f
-                                          name);
77c23f
+                                          sane_name);
77c23f
     }
77c23f
 
77c23f
     /* Don't bother sending NBD_INFO_NAME unless client requested it */
77c23f
@@ -995,8 +1010,10 @@ static int nbd_negotiate_meta_queries(NBDClient *client,
77c23f
 
77c23f
     meta->exp = nbd_export_find(export_name);
77c23f
     if (meta->exp == NULL) {
77c23f
+        g_autofree char *sane_name = nbd_sanitize_name(export_name);
77c23f
+
77c23f
         return nbd_opt_drop(client, NBD_REP_ERR_UNKNOWN, errp,
77c23f
-                            "export '%s' not present", export_name);
77c23f
+                            "export '%s' not present", sane_name);
77c23f
     }
77c23f
 
77c23f
     ret = nbd_opt_read(client, &nb_queries, sizeof(nb_queries), errp);
77c23f
diff --git a/tests/qemu-iotests/143 b/tests/qemu-iotests/143
77c23f
index f649b36195..d2349903b1 100755
77c23f
--- a/tests/qemu-iotests/143
77c23f
+++ b/tests/qemu-iotests/143
77c23f
@@ -58,6 +58,10 @@ _send_qemu_cmd $QEMU_HANDLE \
77c23f
 $QEMU_IO_PROG -f raw -c quit \
77c23f
     "nbd+unix:///no_such_export?socket=$SOCK_DIR/nbd" 2>&1 \
77c23f
     | _filter_qemu_io | _filter_nbd
77c23f
+# Likewise, with longest possible name permitted in NBD protocol
77c23f
+$QEMU_IO_PROG -f raw -c quit \
77c23f
+    "nbd+unix:///$(printf %4096d 1 | tr ' ' a)?socket=$SOCK_DIR/nbd" 2>&1 \
77c23f
+    | _filter_qemu_io | _filter_nbd | sed 's/aaaa*aa/aa--aa/'
77c23f
 
77c23f
 _send_qemu_cmd $QEMU_HANDLE \
77c23f
     "{ 'execute': 'quit' }" \
77c23f
diff --git a/tests/qemu-iotests/143.out b/tests/qemu-iotests/143.out
77c23f
index 1f4001c601..fc9c0a761f 100644
77c23f
--- a/tests/qemu-iotests/143.out
77c23f
+++ b/tests/qemu-iotests/143.out
77c23f
@@ -5,6 +5,8 @@ QA output created by 143
77c23f
 {"return": {}}
77c23f
 qemu-io: can't open device nbd+unix:///no_such_export?socket=SOCK_DIR/nbd: Requested export not available
77c23f
 server reported: export 'no_such_export' not present
77c23f
+qemu-io: can't open device nbd+unix:///aa--aa1?socket=SOCK_DIR/nbd: Requested export not available
77c23f
+server reported: export 'aa--aa...' not present
77c23f
 { 'execute': 'quit' }
77c23f
 {"return": {}}
77c23f
 {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
77c23f
-- 
77c23f
2.27.0
77c23f