Blame SOURCES/redis-CVE-2021-32675.patch

543445
From 5674b0057ff2903d43eaff802017eddf37c360f8 Mon Sep 17 00:00:00 2001
543445
From: Oran Agra <oran@redislabs.com>
543445
Date: Wed, 9 Jun 2021 17:31:39 +0300
543445
Subject: [PATCH] Prevent unauthenticated client from easily consuming lots of
543445
 memory (CVE-2021-32675)
543445
543445
This change sets a low limit for multibulk and bulk length in the
543445
protocol for unauthenticated connections, so that they can't easily
543445
cause redis to allocate massive amounts of memory by sending just a few
543445
characters on the network.
543445
The new limits are 10 arguments of 16kb each (instead of 1m of 512mb)
543445
---
543445
 src/networking.c    | 17 +++++++++++++++++
543445
 src/server.c        |  9 ++-------
543445
 src/server.h        |  1 +
543445
 tests/unit/auth.tcl | 16 ++++++++++++++++
543445
 4 files changed, 36 insertions(+), 7 deletions(-)
543445
543445
diff --git a/src/networking.c b/src/networking.c
543445
index a61678dab2b..b02397c96f4 100644
543445
--- a/src/networking.c
543445
+++ b/src/networking.c
543445
@@ -97,6 +97,15 @@ void linkClient(client *c) {
543445
     raxInsert(server.clients_index,(unsigned char*)&id,sizeof(id),c,NULL);
543445
 }
543445
 
543445
+int authRequired(client *c) {
543445
+    /* Check if the user is authenticated. This check is skipped in case
543445
+     * the default user is flagged as "nopass" and is active. */
543445
+    int auth_required = (!(DefaultUser->flags & USER_FLAG_NOPASS) ||
543445
+                          (DefaultUser->flags & USER_FLAG_DISABLED)) &&
543445
+                        !c->authenticated;
543445
+    return auth_required;
543445
+}
543445
+
543445
 client *createClient(connection *conn) {
543445
     client *c = zmalloc(sizeof(client));
543445
 
543445
@@ -1744,6 +1753,10 @@ int processMultibulkBuffer(client *c) {
543445
             addReplyError(c,"Protocol error: invalid multibulk length");
543445
             setProtocolError("invalid mbulk count",c);
543445
             return C_ERR;
543445
+        } else if (ll > 10 && authRequired(c)) {
543445
+            addReplyError(c, "Protocol error: unauthenticated multibulk length");
543445
+            setProtocolError("unauth mbulk count", c);
543445
+            return C_ERR;
543445
         }
543445
 
543445
         c->qb_pos = (newline-c->querybuf)+2;
543445
@@ -1791,6 +1804,10 @@ int processMultibulkBuffer(client *c) {
543445
                 addReplyError(c,"Protocol error: invalid bulk length");
543445
                 setProtocolError("invalid bulk length",c);
543445
                 return C_ERR;
543445
+            } else if (ll > 16384 && authRequired(c)) {
543445
+                addReplyError(c, "Protocol error: unauthenticated bulk length");
543445
+                setProtocolError("unauth bulk length", c);
543445
+                return C_ERR;
543445
             }
543445
 
543445
             c->qb_pos = newline-c->querybuf+2;
543445
diff --git a/src/server.c b/src/server.c
543445
index 93148f8e3ed..c8768b1824b 100644
543445
--- a/src/server.c
543445
+++ b/src/server.c
543445
@@ -3590,13 +3590,8 @@ int processCommand(client *c) {
543445
     int is_denyloading_command = !(c->cmd->flags & CMD_LOADING) ||
543445
                                  (c->cmd->proc == execCommand && (c->mstate.cmd_inv_flags & CMD_LOADING));
543445
 
543445
-    /* Check if the user is authenticated. This check is skipped in case
543445
-     * the default user is flagged as "nopass" and is active. */
543445
-    int auth_required = (!(DefaultUser->flags & USER_FLAG_NOPASS) ||
543445
-                          (DefaultUser->flags & USER_FLAG_DISABLED)) &&
543445
-                        !c->authenticated;
543445
-    if (auth_required) {
543445
-        /* AUTH and HELLO and no auth modules are valid even in
543445
+    if (authRequired(c)) {
543445
+        /* AUTH and HELLO and no auth commands are valid even in
543445
          * non-authenticated state. */
543445
         if (!(c->cmd->flags & CMD_NO_AUTH)) {
543445
             rejectCommand(c,shared.noautherr);
543445
diff --git a/src/server.h b/src/server.h
543445
index a16f2885829..530355421a8 100644
543445
--- a/src/server.h
543445
+++ b/src/server.h
543445
@@ -1743,6 +1743,7 @@ void protectClient(client *c);
543445
 void unprotectClient(client *c);
543445
 void initThreadedIO(void);
543445
 client *lookupClientByID(uint64_t id);
543445
+int authRequired(client *c);
543445
 
543445
 #ifdef __GNUC__
543445
 void addReplyErrorFormat(client *c, const char *fmt, ...)
543445
diff --git a/tests/unit/auth.tcl b/tests/unit/auth.tcl
543445
index 9080d4bf7e9..530ca7c8d91 100644
543445
--- a/tests/unit/auth.tcl
543445
+++ b/tests/unit/auth.tcl
543445
@@ -24,4 +24,20 @@ start_server {tags {"auth"} overrides {requirepass foobar}} {
543445
         r set foo 100
543445
         r incr foo
543445
     } {101}
543445
+
543445
+    test {For unauthenticated clients multibulk and bulk length are limited} {
543445
+        set rr [redis [srv "host"] [srv "port"] 0 $::tls]
543445
+        $rr write "*100\r\n"
543445
+        $rr flush
543445
+        catch {[$rr read]} e
543445
+        assert_match {*unauthenticated multibulk length*} $e
543445
+        $rr close
543445
+
543445
+        set rr [redis [srv "host"] [srv "port"] 0 $::tls]
543445
+        $rr write "*1\r\n\$100000000\r\n"
543445
+        $rr flush
543445
+        catch {[$rr read]} e
543445
+        assert_match {*unauthenticated bulk length*} $e
543445
+        $rr close
543445
+    }
543445
 }