summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2019-01-29 17:25:02 +0100
committerantirez <antirez@gmail.com>2019-01-29 17:25:05 +0100
commit11f64c68866538db402e3f327ac59789b373cc35 (patch)
tree84905116a38cec7630ac4e0d5ad0484a64c1c34d
parent4380423d40afa9c12e8018b7d5da4fe0f5ce5c3e (diff)
downloadredis-11f64c68866538db402e3f327ac59789b373cc35.tar.gz
ACL: initial design for ACLDescribeUserCommandRules() and helpers.
-rw-r--r--src/acl.c88
1 files changed, 88 insertions, 0 deletions
diff --git a/src/acl.c b/src/acl.c
index ecaae9268..ebfb5a066 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -228,6 +228,94 @@ int ACLSetUserCommandBitsForCategory(user *u, const char *category, int value) {
return C_OK;
}
+/* Return the number of commands allowed (on) and denied (off) for the user 'u'
+ * in the subset of commands flagged with the specified category name.
+ * If the categoty name is not valid, C_ERR is returend, otherwise C_OK is
+ * returned and on and off are populated by reference. */
+int ACLCountCategoryBitsForUser(user *u, unsigned long *on, unsigned long *off,
+ const char *category)
+{
+ uint64_t cflag = ACLGetCommandCategoryFlagByName(category);
+ if (!cflag) return C_ERR;
+
+ *on = *off = 0;
+ dictIterator *di = dictGetIterator(server.orig_commands);
+ dictEntry *de;
+ while ((de = dictNext(di)) != NULL) {
+ struct redisCommand *cmd = dictGetVal(de);
+ if (cmd->flags & cflag) {
+ if (ACLGetUserCommandBit(u,cmd->id))
+ (*on)++;
+ else
+ (*off)++;
+ }
+ }
+ dictReleaseIterator(di);
+ return C_OK;
+}
+
+/* This function returns an SDS string representing the specified user ACL
+ * rules related to command execution, in the same format you could set them
+ * back using ACL SETUSER. The function will return just the set of rules needed
+ * to recreate the user commands bitmap, without including other user flags such
+ * as on/off, passwords and so forth. The returned string always starts with
+ * the +@all or -@all rule, depending on the user bitmap, and is followed, if
+ * needed, by the other rules needed to narrow or extend what the user can do. */
+sds ACLDescribeUserCommandRules(user *u) {
+ sds rules = sdsempty();
+ int additive; /* If true we start from -@all and add, otherwise if
+ false we start from +@all and remove. */
+
+ /* This code is based on a trick: as we generate the rules, we apply
+ * them to a fake user, so that as we go we still know what are the
+ * bit differences we should try to address by emitting more rules. */
+ user fu = {0};
+ user *fakeuser = &fu;
+
+ /* Here we want to understand if we should start with +@all and remove
+ * the commands corresponding to the bits that are not set in the user
+ * commands bitmap, or the contrary. Note that semantically the two are
+ * different. For instance starting with +@all and subtracting, the user
+ * will be able to execute future commands, while -@all and adding will just
+ * allow the user the run the selected commands and/or categories.
+ * How do we test for that? We use the trick of a reserved command ID bit
+ * that is set only by +@all (and its alias "allcommands"). */
+ if (ACLUserCanExecuteFutureCommands(u)) {
+ additive = 0;
+ rules = sdscat(rules,"+@all ");
+ } else {
+ additive = 1;
+ rules = sdscat(rules,"-@all ");
+ }
+
+ /* Try to add or subtract each category one after the other. Often a
+ * single category will not perfectly match the set of commands into
+ * it, so at the end we do a final pass adding/removing the single commands
+ * needed to make the bitmap exactly match. */
+ for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {
+ unsigned long on, off;
+ ACLCountCategoryBitsForUser(u,&on,&off,ACLCommandCategories[j].name);
+ if ((additive && on > off) || (!additive && off > on)) {
+ rules = sdscatlen(rules, additive ? "+@" : "-@", 2);
+ rules = sdscat(rules,ACLCommandCategories[j].name);
+ rules = sdscatlen(rules," ",1);
+ }
+ }
+
+ /* Fix the final ACLs with single commands differences. */
+
+ /* Trim the final useless space. */
+
+ /* This is technically not needed, but we want to verify that now the
+ * predicted bitmap is exactly the same as the user bitmap, and abort
+ * otherwise, because aborting is better than a security risk in this
+ * code path. */
+ serverAssert(memcmp(fakeuser->allowed_commands,
+ u->allowed_commands,
+ sizeof(u->allowed_commands)) == 0);
+ return rules;
+}
+
/* Get a command from the original command table, that is not affected
* by the command renaming operations: we base all the ACL work from that
* table, so that ACLs are valid regardless of command renaming. */