summaryrefslogtreecommitdiff
path: root/src/pubsub.c
diff options
context:
space:
mode:
authorItamar Haber <itamar@redislabs.com>2020-12-01 14:21:39 +0200
committerGitHub <noreply@github.com>2020-12-01 14:21:39 +0200
commitc1b1e8c329567e90ec0edcb8c176e9f58a7d7435 (patch)
treeb267d8bc804a6f8c0c382efe2d6e93496a2d10ba /src/pubsub.c
parentc85bf2352d2a3dd04872124d33066403b114a7e7 (diff)
downloadredis-c1b1e8c329567e90ec0edcb8c176e9f58a7d7435.tar.gz
Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923. This PR appropriates the special `&` symbol (because `@` and `*` are taken), followed by a literal value or pattern for describing the Pub/Sub patterns that an ACL user can interact with. It is similar to the existing key patterns mechanism in function (additive) and implementation (copy-pasta). It also adds the allchannels and resetchannels ACL keywords, naturally. The default user is given allchannels permissions, whereas new users get whatever is defined by the acl-pubsub-default configuration directive. For backward compatibility in 6.2, the default of this directive is allchannels but this is likely to be changed to resetchannels in the next major version for stronger default security settings. Unless allchannels is set for the user, channel access permissions are checked as follows : * Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the argumentative channel name(s) exists for the user. * Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument literally exist(s) in the user's list. Such failures are logged to the ACL log. Runtime changes to channel permissions for a user with existing subscribing clients cause said clients to disconnect unless the new permissions permit the connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be disconnected. Notes/questions: * UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons for touching them.
Diffstat (limited to 'src/pubsub.c')
-rw-r--r--src/pubsub.c29
1 files changed, 25 insertions, 4 deletions
diff --git a/src/pubsub.c b/src/pubsub.c
index b7f7d9673..9e41464bf 100644
--- a/src/pubsub.c
+++ b/src/pubsub.c
@@ -353,13 +353,29 @@ int pubsubPublishMessage(robj *channel, robj *message) {
return receivers;
}
+/* This wraps handling ACL channel permissions for the given client. */
+int pubsubCheckACLPermissionsOrReply(client *c, int idx, int count, int literal) {
+ /* Check if the user can run the command according to the current
+ * ACLs. */
+ int acl_chanpos;
+ int acl_retval = ACLCheckPubsubPerm(c,idx,count,literal,&acl_chanpos);
+ if (acl_retval == ACL_DENIED_CHANNEL) {
+ addACLLogEntry(c,acl_retval,acl_chanpos,NULL);
+ addReplyError(c,
+ "-NOPERM this user has no permissions to access "
+ "one of the channels used as arguments");
+ }
+ return acl_retval;
+}
+
/*-----------------------------------------------------------------------------
* Pubsub commands implementation
*----------------------------------------------------------------------------*/
+/* SUBSCRIBE channel [channel ...] */
void subscribeCommand(client *c) {
int j;
-
+ if (pubsubCheckACLPermissionsOrReply(c,1,c->argc-1,0) != ACL_OK) return;
if ((c->flags & CLIENT_DENY_BLOCKING) && !(c->flags & CLIENT_MULTI)) {
/**
* A client that has CLIENT_DENY_BLOCKING flag on
@@ -368,7 +384,7 @@ void subscribeCommand(client *c) {
* Notice that we have a special treatment for multi because of
* backword compatibility
*/
- addReplyError(c, "subscribe is not allow on DENY BLOCKING client");
+ addReplyError(c, "SUBSCRIBE isn't allowed for a DENY BLOCKING client");
return;
}
@@ -377,6 +393,7 @@ void subscribeCommand(client *c) {
c->flags |= CLIENT_PUBSUB;
}
+/* UNSUBSCRIBE [channel [channel ...]] */
void unsubscribeCommand(client *c) {
if (c->argc == 1) {
pubsubUnsubscribeAllChannels(c,1);
@@ -389,9 +406,10 @@ void unsubscribeCommand(client *c) {
if (clientSubscriptionsCount(c) == 0) c->flags &= ~CLIENT_PUBSUB;
}
+/* PSUBSCRIBE pattern [pattern ...] */
void psubscribeCommand(client *c) {
int j;
-
+ if (pubsubCheckACLPermissionsOrReply(c,1,c->argc-1,1) != ACL_OK) return;
if ((c->flags & CLIENT_DENY_BLOCKING) && !(c->flags & CLIENT_MULTI)) {
/**
* A client that has CLIENT_DENY_BLOCKING flag on
@@ -400,7 +418,7 @@ void psubscribeCommand(client *c) {
* Notice that we have a special treatment for multi because of
* backword compatibility
*/
- addReplyError(c, "PSUBSCRIBE is not allowed for DENY BLOCKING client");
+ addReplyError(c, "PSUBSCRIBE isn't allowed for a DENY BLOCKING client");
return;
}
@@ -409,6 +427,7 @@ void psubscribeCommand(client *c) {
c->flags |= CLIENT_PUBSUB;
}
+/* PUNSUBSCRIBE [pattern [pattern ...]] */
void punsubscribeCommand(client *c) {
if (c->argc == 1) {
pubsubUnsubscribeAllPatterns(c,1);
@@ -421,7 +440,9 @@ void punsubscribeCommand(client *c) {
if (clientSubscriptionsCount(c) == 0) c->flags &= ~CLIENT_PUBSUB;
}
+/* PUBLISH <channel> <message> */
void publishCommand(client *c) {
+ if (pubsubCheckACLPermissionsOrReply(c,1,1,0) != ACL_OK) return;
int receivers = pubsubPublishMessage(c->argv[1],c->argv[2]);
if (server.cluster_enabled)
clusterPropagatePublish(c->argv[1],c->argv[2]);