diff --git a/src/acl.c b/src/acl.c index eb551958..742860e6 100644 --- a/src/acl.c +++ b/src/acl.c @@ -229,15 +229,41 @@ user *ACLGetUserByName(const char *name, size_t namelen) { * If the user can execute the command C_OK is returned, otherwise * C_ERR is returned. */ int ACLCheckCommandPerm(client *c) { + user *u = c->user; + uint64_t id = c->cmd->id; + /* If there is no associated user, the connection can run anything. */ - if (c->user == NULL) return C_OK; + if (u == NULL) return C_OK; + + /* We have to deny every command with an ID that overflows the Redis + * internal structures. Very unlikely to happen. */ + if (c->cmd->id >= USER_MAX_COMMAND_BIT) return C_ERR; /* Check if the user can execute this command. */ - if (!(c->user->flags & USER_FLAG_ALLCOMMANDS)) { + if (!(u->flags & USER_FLAG_ALLCOMMANDS)) { + uint64_t wordid = id / sizeof(u->allowed_commands[0]) / 8; + uint64_t bit = 1 << (id % (sizeof(u->allowed_commands[0] * 8))); + /* If the bit is not set we have to check further, in case the + * command is allowed just with that specific subcommand. */ + if (!(u->allowed_commands[wordid] & bit)) { + /* Check if the subcommand matches. */ + if (u->allowed_subcommands == NULL || c->argc < 2) return C_ERR; + long subid = 0; + while (1) { + if (u->allowed_subcommands[id][subid] == NULL) return C_ERR; + if (!strcasecmp(c->argv[1]->ptr, + u->allowed_subcommands[id][subid])) + break; /* Subcommand match found. Stop here. */ + subid++; + } + } } - /* Check if the user can execute touch this keys. */ - if (!(c->user->flags & USER_FLAG_ALLKEYS)) { + /* Check if the user can execute commands explicitly touching the keys + * mentioned in the command arguments. */ + if (!(c->user->flags & USER_FLAG_ALLKEYS) && + (c->cmd->getkeys_proc || c->cmd->firstkey)) + { } /* If we survived all the above checks, the user can execute the diff --git a/src/server.h b/src/server.h index e2204231..70cb0040 100644 --- a/src/server.h +++ b/src/server.h @@ -710,7 +710,8 @@ typedef struct readyList { /* This structure represents a Redis user. This is useful for ACLs, the * user is associated to the connection after the connection is authenticated. * If there is no associated user, the connection uses the default user. */ -#define USER_MAX_COMMAND_BIT 1024 +#define USER_MAX_COMMAND_BIT 1024 /* The first *not valid* bit that + would overflow. So check for >= */ #define USER_FLAG_ENABLED (1<<0) /* The user is active. */ #define USER_FLAG_ALLKEYS (1<<1) /* The user can mention any key. */ #define USER_FLAG_ALLCOMMANDS (1<<2) /* The user can run all commands. */