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

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