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