Fix Read/Write key pattern selector (CVE-2024-51741)

The explanation on the original commit was wrong. Key based access must
have a `~` in order to correctly configure whey key prefixes to apply
the selector to. If this is missing, a server assert will be triggered
later.

Cherry-picked and squashed from relevant Valkey changes.

Signed-off-by: Madelyn Olson <madelyneolson@gmail.com>
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Co-authored-by: YaacovHazan <yaacov.hazan@redis.com>
This commit is contained in:
Drew DeVault 2025-01-07 18:27:01 +01:00
parent a8edd3f6ac
commit ba5dcb3b16
2 changed files with 48 additions and 29 deletions

View File

@ -1068,19 +1068,24 @@ int ACLSetSelector(aclSelector *selector, const char* op, size_t oplen) {
int flags = 0; int flags = 0;
size_t offset = 1; size_t offset = 1;
if (op[0] == '%') { if (op[0] == '%') {
int perm_ok = 1;
for (; offset < oplen; offset++) { for (; offset < oplen; offset++) {
if (toupper(op[offset]) == 'R' && !(flags & ACL_READ_PERMISSION)) { if (toupper(op[offset]) == 'R' && !(flags & ACL_READ_PERMISSION)) {
flags |= ACL_READ_PERMISSION; flags |= ACL_READ_PERMISSION;
} else if (toupper(op[offset]) == 'W' && !(flags & ACL_WRITE_PERMISSION)) { } else if (toupper(op[offset]) == 'W' && !(flags & ACL_WRITE_PERMISSION)) {
flags |= ACL_WRITE_PERMISSION; flags |= ACL_WRITE_PERMISSION;
} else if (op[offset] == '~' && flags) { } else if (op[offset] == '~') {
offset++; offset++;
break; break;
} else { } else {
perm_ok = 0;
break;
}
}
if (!flags || !perm_ok) {
errno = EINVAL; errno = EINVAL;
return C_ERR; return C_ERR;
} }
}
} else { } else {
flags = ACL_ALL_PERMISSION; flags = ACL_ALL_PERMISSION;
} }

View File

@ -123,9 +123,23 @@ start_server {tags {"acl external:skip"}} {
} }
test {Validate read and write permissions format} { test {Validate read and write permissions format} {
catch {r ACL SETUSER key-permission-RW %~} err # Regression tests for CVE-2024-51741
set err assert_error "ERR Error in ACL SETUSER modifier '%~': Syntax error" {r ACL SETUSER invalid %~}
} {ERR Error in ACL SETUSER modifier '%~': Syntax error} assert_error "ERR Error in ACL SETUSER modifier '%': Syntax error" {r ACL SETUSER invalid %}
}
test {Validate key permissions format - empty and omitted pattern} {
# Empty pattern results with access to only the empty key
r ACL SETUSER key-permission-no-key on nopass %RW~ +@all
assert_equal "User key-permission-no-key has no permissions to access the 'x' key" [r ACL DRYRUN key-permission-no-key GET x]
assert_equal "OK" [r ACL DRYRUN key-permission-no-key GET ""]
# This is incorrect syntax, it should have `~`, but we'll allow it for compatibility since it does something
r ACL SETUSER key-permission-omit on nopass %RW +@all
assert_equal "User key-permission-omit has no permissions to access the 'x' key" [r ACL DRYRUN key-permission-omit GET x]
assert_equal "OK" [r ACL DRYRUN key-permission-omit GET ""]
# Assert these two are equivalent
assert_equal [r ACL GETUSER key-permission-omit] [r ACL GETUSER key-permission-no-key]
}
test {Test separate read and write permissions on different selectors are not additive} { 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)" r ACL SETUSER key-permission-RW-selector on nopass "(%R~read* +@all)" "(%W~write* +@all)"