From 15e212bf69de28d2b4585aa79cc2a40f49e4a94d Mon Sep 17 00:00:00 2001
From: YaacovHazan <yaacov.hazan@redis.com>
Date: Sun, 15 Dec 2024 11:27:48 +0200
Subject: [PATCH] Fix Read/Write key pattern selector (CVE-2024-51741)

The '%' rule must contain one or both of R/W

CVE: CVE-2024-51741

Upstream-Status: Backport [https://github.com/redis/redis/commit/15e212bf69de28d2b4585aa79cc2a40f49e4a94d]

Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
---
 src/acl.c             | 11 ++++++++---
 tests/unit/acl-v2.tcl | 26 ++++++++++++++++++++++++++
 2 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/src/acl.c b/src/acl.c
index 6b53d90..ed6dc97 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -1031,19 +1031,24 @@ int ACLSetSelector(aclSelector *selector, const char* op, size_t oplen) {
         int flags = 0;
         size_t offset = 1;
         if (op[0] == '%') {
+            int perm_ok = 1;
             for (; offset < oplen; offset++) {
                 if (toupper(op[offset]) == 'R' && !(flags & ACL_READ_PERMISSION)) {
                     flags |= ACL_READ_PERMISSION;
                 } else if (toupper(op[offset]) == 'W' && !(flags & ACL_WRITE_PERMISSION)) {
                     flags |= ACL_WRITE_PERMISSION;
-                } else if (op[offset] == '~' && flags) {
+                } else if (op[offset] == '~') {
                     offset++;
                     break;
                 } else {
-                    errno = EINVAL;
-                    return C_ERR;
+                    perm_ok = 0;
+                    break;
                 }
             }
+            if (!flags || !perm_ok) {
+                errno = EINVAL;
+                return C_ERR;
+            }
         } else {
             flags = ACL_ALL_PERMISSION;
         }
diff --git a/tests/unit/acl-v2.tcl b/tests/unit/acl-v2.tcl
index d836f9c..0b83b89 100644
--- a/tests/unit/acl-v2.tcl
+++ b/tests/unit/acl-v2.tcl
@@ -107,6 +107,32 @@ start_server {tags {"acl external:skip"}} {
         assert_match "*NOPERM*keys*" $err
     }
 
+    test {Validate read and write permissions format - empty permission} {
+        catch {r ACL SETUSER key-permission-RW %~} err
+        set err
+    } {ERR Error in ACL SETUSER modifier '%~': Syntax error}
+
+    test {Validate read and write permissions format - empty selector} {
+        catch {r ACL SETUSER key-permission-RW %} err
+        set err
+    } {ERR Error in ACL SETUSER modifier '%': Syntax error}
+
+    test {Validate read and write permissions format - empty pattern} {
+        # Empty pattern results with R/W access to no key
+        r ACL SETUSER key-permission-RW on nopass %RW~ +@all
+        $r2 auth key-permission-RW password
+        catch {$r2 SET x 5} err
+        set err
+    } {NOPERM No permissions to access a key}
+
+    test {Validate read and write permissions format - no pattern} {
+        # No pattern results with R/W access to no key (currently we accept this syntax error)
+        r ACL SETUSER key-permission-RW on nopass %RW +@all
+        $r2 auth key-permission-RW password
+        catch {$r2 SET x 5} err
+        set err
+    } {NOPERM No permissions to access a key}
+
     test {Test separate read and write permissions on different selectors are not additive} {
         r ACL SETUSER key-permission-RW-selector on nopass "(%R~read* +@all)" "(%W~write* +@all)"
         $r2 auth key-permission-RW-selector password
-- 
2.40.0

