Skip to content

Commit e3eca62

Browse files
committed
Extend serverCommand with get_dbid_args, unify ACL checks
Signed-off-by: Daniil Kashapov <[email protected]>
1 parent f19ec2f commit e3eca62

File tree

11 files changed

+529
-502
lines changed

11 files changed

+529
-502
lines changed

src/acl.c

Lines changed: 44 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ typedef struct {
181181
ALLCHANNELS is set in the user. */
182182
sds command_rules; /* A string representation of the ordered categories and commands, this
183183
* is used to regenerate the original ACL string for display. */
184-
intset *dbs; /* Set of database ids, based on SELECTOR_FLAG_DBLIST_NEGATED
185-
it is going to be allowed or disallowed database ids */
184+
intset *dbs; /* Set of allowed database ids. If set is NULL or empty the user
185+
* cannot access any database, unless the flag ALLDBS is set. */
186186
} aclSelector;
187187

188188
static void ACLResetFirstArgsForCommand(aclSelector *selector, unsigned long id);
@@ -821,12 +821,7 @@ static sds ACLDescribeSelector(aclSelector *selector) {
821821
if (selector->flags & SELECTOR_FLAG_ALLDBS) {
822822
res = sdscatlen(res, "alldbs ", 7);
823823
} else {
824-
if (selector->flags & SELECTOR_FLAG_DBLIST_NEGATED) {
825-
res = sdscatlen(res, "db!=", 4);
826-
} else {
827-
res = sdscatlen(res, "db=", 3);
828-
}
829-
824+
res = sdscatlen(res, "db=", 3);
830825
uint32_t len = intsetLen(selector->dbs);
831826
for (uint32_t i = 0; i < len; i++) {
832827
int64_t dbid;
@@ -965,7 +960,7 @@ static void ACLAddAllowedFirstArg(aclSelector *selector, unsigned long id, const
965960
}
966961

967962
/* Helper function to set database permissions for a selector */
968-
static int ACLSetSelectorDatabasePermissions(aclSelector *selector, const char *dbs_str, int negated) {
963+
static int ACLSetSelectorDatabasePermissions(aclSelector *selector, const char *dbs_str) {
969964
selector->flags &= ~SELECTOR_FLAG_ALLDBS;
970965

971966
intset *new_dbs = intsetNew();
@@ -974,20 +969,13 @@ static int ACLSetSelectorDatabasePermissions(aclSelector *selector, const char *
974969
return C_ERR;
975970
}
976971

977-
if (negated) {
978-
selector->flags |= SELECTOR_FLAG_DBLIST_NEGATED;
979-
} else {
980-
selector->flags &= ~SELECTOR_FLAG_DBLIST_NEGATED;
981-
}
982-
983972
char *dblist = zstrdup(dbs_str);
984973
if (!dblist) {
985974
intsetFree(new_dbs);
986975
errno = ENOMEM;
987976
return C_ERR;
988977
}
989978

990-
991979
// Reject empty list, trailing commas or more than 1 consecutive commas
992980
if (strlen(dblist) == 0 || dblist[0] == ',' ||
993981
dblist[strlen(dblist) - 1] == ',' || strstr(dblist, ",,")) {
@@ -1100,11 +1088,8 @@ static aclSelector *aclCreateSelectorFromOpSet(const char *opset, size_t opsetle
11001088
* db=<dbid> Add database ID to the set of allowed IDs, any other database ID
11011089
* will be disallowed. May be used with `,` for assingning multiple
11021090
* allowed IDs (e.g "db=1,2,3").
1103-
* db!=<dbid> Add database ID to the set of disallowed IDs, any other database ID
1104-
* will be disallowed. May be used with `,` for assingning multiple
1105-
* disallowed IDs (e.g "db!=4,5,6").
11061091
* alldbs Allow access to all databases.
1107-
* resetdbs Flush the set of allowed/disallowed database IDs, revert to default
1092+
* resetdbs Flush the set of allowed database IDs, revert to default
11081093
* behaviour (all databases are allowed).
11091094
*/
11101095
static int ACLSetSelector(aclSelector *selector, const char *op, size_t oplen) {
@@ -1132,11 +1117,8 @@ static int ACLSetSelector(aclSelector *selector, const char *op, size_t oplen) {
11321117
intsetFree(selector->dbs);
11331118
selector->dbs = intsetNew();
11341119
}
1135-
} else if (strncmp(op, "db=", 3) == 0) {
1136-
int result = ACLSetSelectorDatabasePermissions(selector, op + 3, 0);
1137-
if (result != C_OK) return result;
1138-
} else if (strncmp(op, "db!=", 4) == 0) {
1139-
int result = ACLSetSelectorDatabasePermissions(selector, op + 4, 1);
1120+
} else if (strncmp(op, "db=", 3) == 0 || strncmp(op, "DB=", 3) == 0) {
1121+
int result = ACLSetSelectorDatabasePermissions(selector, op + 3);
11401122
if (result != C_OK) return result;
11411123
} else if (!strcasecmp(op, "allcommands") || !strcasecmp(op, "+@all")) {
11421124
memset(selector->allowed_commands, 255, sizeof(selector->allowed_commands));
@@ -1750,13 +1732,11 @@ int ACLSelectorCanAccessDb(aclSelector *selector, long long dbid) {
17501732
if (selector->flags & SELECTOR_FLAG_ALLDBS)
17511733
return 1;
17521734

1753-
if (dbid < 0 || dbid >= server.dbnum)
1735+
if (dbid < 0 || dbid >= server.dbnum || !selector->dbs)
17541736
return 0;
17551737

17561738
int found = intsetFind(selector->dbs, (int64_t)dbid);
17571739

1758-
if (selector->flags & SELECTOR_FLAG_DBLIST_NEGATED)
1759-
return !found;
17601740
return found;
17611741
}
17621742

@@ -1792,41 +1772,43 @@ static int ACLSelectorCheckCmd(aclSelector *selector,
17921772
int dbid) {
17931773
uint64_t id = cmd->id;
17941774
int ret;
1775+
1776+
/* Check database level permissions based on command flags. */
17951777
if (cmd->flags & CMD_CROSS_DB) {
1796-
if (cmd->proc == selectCommand && argc > 1) {
1797-
long long target_db;
1798-
if (getLongLongFromObject(argv[1], &target_db) == C_OK) {
1799-
if (target_db >= 0 && target_db < server.dbnum) {
1800-
dbid = (int)target_db;
1778+
if (cmd->get_dbid_args) {
1779+
int count = 0;
1780+
int *dbids = cmd->get_dbid_args(argv, argc, &count);
1781+
if (dbids) {
1782+
for (int i = 0; i < count; i++) {
1783+
if (!ACLSelectorCanAccessDb(selector, dbids[i])) {
1784+
if (keyidxptr) {
1785+
if (cmd->proc == selectCommand)
1786+
*keyidxptr = 1;
1787+
else if (cmd->proc == moveCommand)
1788+
*keyidxptr = 2;
1789+
else if (cmd->proc == swapdbCommand)
1790+
*keyidxptr = (i == 0) ? 1 : 2;
1791+
else
1792+
*keyidxptr = 0;
1793+
}
1794+
zfree(dbids);
1795+
return ACL_DENIED_DB;
1796+
}
18011797
}
1798+
zfree(dbids);
18021799
}
1803-
} else if (cmd->proc == swapdbCommand && argc > 2) {
1804-
long long db1, db2;
1805-
if (getLongLongFromObject(argv[1], &db1) == C_OK && getLongLongFromObject(argv[2], &db2) == C_OK &&
1806-
(!ACLSelectorCanAccessDb(selector, db1) || !ACLSelectorCanAccessDb(selector, db2))) {
1807-
return ACL_DENIED_DB;
1808-
}
1809-
} else if (cmd->proc == moveCommand && argc > 2) {
1810-
long long target_db;
1811-
if (getLongLongFromObject(argv[2], &target_db) == C_OK &&
1812-
!ACLSelectorCanAccessDb(selector, target_db)) {
1813-
return ACL_DENIED_DB;
1814-
}
1800+
} else {
1801+
/* Command has flag CMD_CROSS_DB but has no function to get its db id args */
1802+
return ACL_NOT_IMPLEMENTED;
18151803
}
1816-
}
1817-
1818-
if ((cmd->flags & CMD_ALL_DBS) && !(selector->flags & SELECTOR_FLAG_ALLDBS)) {
1804+
} else if ((cmd->flags & CMD_ALL_DBS) && !(selector->flags & SELECTOR_FLAG_ALLDBS)) {
18191805
for (int i = 0; i < server.dbnum; i++) {
18201806
if (!ACLSelectorCanAccessDb(selector, i)) {
18211807
return ACL_DENIED_DB;
18221808
}
18231809
}
1824-
}
1825-
1826-
if (!ACLSelectorCanAccessDb(selector, dbid)) {
1827-
if (keyidxptr) {
1828-
*keyidxptr = (cmd->proc == selectCommand) ? 1 : 0;
1829-
}
1810+
} else if (!ACLSelectorCanAccessDb(selector, dbid)) {
1811+
if (keyidxptr) *keyidxptr = 0;
18301812
return ACL_DENIED_DB;
18311813
}
18321814

@@ -2767,6 +2749,7 @@ static void ACLUpdateInfoMetrics(int reason) {
27672749
server.acl_info.invalid_channel_accesses++;
27682750
} else if (reason == ACL_DENIED_DB) {
27692751
server.acl_info.invalid_db_accesses++;
2752+
} else if (reason == ACL_NOT_IMPLEMENTED) {
27702753
} else {
27712754
serverPanic("Unknown ACL_DENIED encoding");
27722755
}
@@ -2904,8 +2887,14 @@ sds getAclErrorMessage(int acl_res, user *user, struct serverCommand *cmd, sds e
29042887
"database %s",
29052888
user->name, errored_val);
29062889
} else {
2907-
return sdsnew("No permissions to access current database");
2890+
return sdsnew("No permissions to access database");
29082891
}
2892+
case ACL_NOT_IMPLEMENTED:
2893+
return sdscatfmt(sdsempty(),
2894+
"Command '%S' has flag CMD_CROSS_DB "
2895+
"but has no function to get its database id args, "
2896+
"see serverCommand in server.h",
2897+
cmd->fullname);
29092898
}
29102899
serverPanic("Reached deadcode on getAclErrorMessage");
29112900
}

src/cli_commands.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
/* Definitions to configure commands.c to generate the above structs. */
55
#define MAKE_CMD(name, summary, complexity, since, doc_flags, replaced, deprecated, group, group_enum, history, \
6-
num_history, tips, num_tips, function, arity, flags, acl, key_specs, key_specs_num, get_keys, \
7-
numargs) \
6+
num_history, tips, num_tips, function, arity, flags, acl, get_dbid_args, key_specs, \
7+
key_specs_num, get_keys, numargs) \
88
name, summary, group, since, numargs
99
#define MAKE_ARG(name, type, key_spec_index, token, summary, since, flags, numsubargs, deprecated_since) \
1010
name, type, token, since, flags, numsubargs

src/commands.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
#include "server.h"
33

44
#define MAKE_CMD(name, summary, complexity, since, doc_flags, replaced, deprecated, group, group_enum, history, \
5-
num_history, tips, num_tips, function, arity, flags, acl, key_specs, key_specs_num, get_keys, \
6-
numargs) \
5+
num_history, tips, num_tips, function, arity, flags, acl, get_dbid_args, key_specs, \
6+
key_specs_num, get_keys, numargs) \
77
name, summary, complexity, since, doc_flags, replaced, deprecated, group_enum, history, num_history, tips, \
8-
num_tips, function, arity, flags, acl, key_specs, key_specs_num, get_keys, numargs
8+
num_tips, function, arity, flags, acl, get_dbid_args, key_specs, key_specs_num, get_keys, numargs
99
#define MAKE_ARG(name, type, key_spec_index, token, summary, since, flags, numsubargs, deprecated_since) \
1010
name, type, key_spec_index, token, summary, since, flags, deprecated_since, numsubargs
1111
#define COMMAND_STRUCT serverCommand

0 commit comments

Comments
 (0)