Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
4078755d
Commit
4078755d
authored
Jan 18, 2019
by
antirez
Browse files
ACL: setting single commands ACLs implementation.
parent
10f12129
Changes
1
Hide whitespace changes
Inline
Side-by-side
src/acl.c
View file @
4078755d
...
@@ -115,6 +115,55 @@ user *ACLCreateUser(const char *name, size_t namelen) {
...
@@ -115,6 +115,55 @@ user *ACLCreateUser(const char *name, size_t namelen) {
return
u
;
return
u
;
}
}
/* Given a command ID, this function set by reference 'word' and 'bit'
* so that user->allowed_commands[word] will address the right word
* where the corresponding bit for the provided ID is stored, and
* so that user->allowed_commands[word]&bit will identify that specific
* bit. The function returns C_ERR in case the specified ID overflows
* the bitmap in the user representation. */
int
ACLGetCommandBitCoordinates
(
unsigned
long
id
,
uint64_t
*
word
,
uint64_t
*
bit
)
{
if
(
id
>=
USER_MAX_COMMAND_BIT
)
return
C_ERR
;
*
word
=
id
/
sizeof
(
uint64_t
)
/
8
;
*
bit
=
1
<<
(
id
%
(
sizeof
(
uint64_t
)
*
8
));
return
C_OK
;
}
/* Check if the specified command bit is set for the specified user.
* The function returns 1 is the bit is set or 0 if it is not.
* Note that this function does not check the ALLCOMMANDS flag of the user
* but just the lowlevel bitmask.
*
* If the bit overflows the user internal represetation, zero is returned
* in order to disallow the execution of the command in such edge case. */
int
ACLGetUserCommandBit
(
user
*
u
,
unsigned
long
id
)
{
uint64_t
word
,
bit
;
if
(
ACLGetCommandBitCoordinates
(
id
,
&
word
,
&
bit
)
==
C_ERR
)
return
0
;
return
u
->
allowed_commands
[
word
]
&
bit
;
}
/* Set the specified command bit for the specified user to 'value' (0 or 1).
* If the bit overflows the user internal represetation, no operation
* is performed. */
void
ACLSetUserCommandBit
(
user
*
u
,
unsigned
long
id
,
int
value
)
{
uint64_t
word
,
bit
;
if
(
ACLGetCommandBitCoordinates
(
id
,
&
word
,
&
bit
)
==
C_ERR
)
return
;
if
(
value
)
u
->
allowed_commands
[
word
]
|=
bit
;
else
u
->
allowed_commands
[
word
]
&=
~
bit
;
}
/* 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. */
struct
redisCommand
*
ACLLookupCommand
(
const
char
*
name
)
{
struct
redisCommand
*
cmd
;
sds
sdsname
=
sdsnew
(
name
);
cmd
=
dictFetchValue
(
server
.
orig_commands
,
sdsname
);
sdsfree
(
sdsname
);
return
cmd
;
}
/* Set user properties according to the string "op". The following
/* Set user properties according to the string "op". The following
* is a description of what different strings will do:
* is a description of what different strings will do:
*
*
...
@@ -170,6 +219,11 @@ user *ACLCreateUser(const char *name, size_t namelen) {
...
@@ -170,6 +219,11 @@ user *ACLCreateUser(const char *name, size_t namelen) {
* The function returns C_OK if the action to perform was understood because
* The function returns C_OK if the action to perform was understood because
* the 'op' string made sense. Otherwise C_ERR is returned if the operation
* the 'op' string made sense. Otherwise C_ERR is returned if the operation
* is unknown or has some syntax error.
* is unknown or has some syntax error.
*
* When an error is returned, errno is set to the following values:
*
* EINVAL: The specified opcode is not understood.
* ENOENT: The command name provided with + or - is not known.
*/
*/
int
ACLSetUser
(
user
*
u
,
const
char
*
op
,
ssize_t
oplen
)
{
int
ACLSetUser
(
user
*
u
,
const
char
*
op
,
ssize_t
oplen
)
{
if
(
oplen
==
-
1
)
oplen
=
strlen
(
op
);
if
(
oplen
==
-
1
)
oplen
=
strlen
(
op
);
...
@@ -210,7 +264,23 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
...
@@ -210,7 +264,23 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
/* Avoid re-adding the same pattern multiple times. */
/* Avoid re-adding the same pattern multiple times. */
if
(
ln
==
NULL
)
listAddNodeTail
(
u
->
patterns
,
newpat
);
if
(
ln
==
NULL
)
listAddNodeTail
(
u
->
patterns
,
newpat
);
u
->
flags
&=
~
USER_FLAG_ALLKEYS
;
u
->
flags
&=
~
USER_FLAG_ALLKEYS
;
}
else
if
(
op
[
0
]
==
'+'
&&
op
[
1
]
!=
'@'
)
{
if
(
ACLLookupCommand
(
op
+
1
)
==
NULL
)
{
errno
=
ENOENT
;
return
C_ERR
;
}
unsigned
long
id
=
ACLGetCommandID
(
op
+
1
);
ACLSetUserCommandBit
(
u
,
id
,
1
);
}
else
if
(
op
[
0
]
==
'-'
&&
op
[
1
]
!=
'@'
)
{
if
(
ACLLookupCommand
(
op
+
1
)
==
NULL
)
{
errno
=
ENOENT
;
return
C_ERR
;
}
unsigned
long
id
=
ACLGetCommandID
(
op
+
1
);
ACLSetUserCommandBit
(
u
,
id
,
0
);
u
->
flags
&=
~
USER_FLAG_ALLCOMMANDS
;
}
else
{
}
else
{
errno
=
EINVAL
;
return
C_ERR
;
return
C_ERR
;
}
}
return
C_OK
;
return
C_OK
;
...
@@ -305,32 +375,6 @@ user *ACLGetUserByName(const char *name, size_t namelen) {
...
@@ -305,32 +375,6 @@ user *ACLGetUserByName(const char *name, size_t namelen) {
return
myuser
;
return
myuser
;
}
}
/* Given a command ID, this function set by reference 'word' and 'bit'
* so that user->allowed_commands[word] will address the right word
* where the corresponding bit for the provided ID is stored, and
* so that user->allowed_commands[word]&bit will identify that specific
* bit. The function returns C_ERR in case the specified ID overflows
* the bitmap in the user representation. */
int
ACLGetCommandBitCoordinates
(
unsigned
long
id
,
uint64_t
*
word
,
uint64_t
*
bit
)
{
if
(
id
>=
USER_MAX_COMMAND_BIT
)
return
C_ERR
;
*
word
=
id
/
sizeof
(
uint64_t
)
/
8
;
*
bit
=
1
<<
(
id
%
(
sizeof
(
uint64_t
)
*
8
));
return
C_OK
;
}
/* Check if the specified command bit is set for the specified user.
* The function returns 1 is the bit is set or 0 if it is not.
* Note that this function does not check the ALLCOMMANDS flag of the user
* but just the lowlevel bitmask.
*
* If the bit overflows the user internal represetation, zero is returned
* in order to disallow the execution of the command in such edge case. */
int
ACLGetUserCommandBit
(
user
*
u
,
unsigned
long
id
)
{
uint64_t
word
,
bit
;
if
(
ACLGetCommandBitCoordinates
(
id
,
&
word
,
&
bit
)
==
C_ERR
)
return
0
;
return
u
->
allowed_commands
[
word
]
&
bit
;
}
/* Check if the command ready to be excuted in the client 'c', and already
/* Check if the command ready to be excuted in the client 'c', and already
* referenced by c->cmd, can be executed by this client according to the
* referenced by c->cmd, can be executed by this client according to the
* ACls associated to the client user c->user.
* ACls associated to the client user c->user.
...
@@ -424,9 +468,12 @@ void aclCommand(client *c) {
...
@@ -424,9 +468,12 @@ void aclCommand(client *c) {
serverAssert
(
u
!=
NULL
);
serverAssert
(
u
!=
NULL
);
for
(
int
j
=
3
;
j
<
c
->
argc
;
j
++
)
{
for
(
int
j
=
3
;
j
<
c
->
argc
;
j
++
)
{
if
(
ACLSetUser
(
u
,
c
->
argv
[
j
]
->
ptr
,
sdslen
(
c
->
argv
[
j
]
->
ptr
))
!=
C_OK
)
{
if
(
ACLSetUser
(
u
,
c
->
argv
[
j
]
->
ptr
,
sdslen
(
c
->
argv
[
j
]
->
ptr
))
!=
C_OK
)
{
char
*
errmsg
=
"wrong format"
;
if
(
errno
==
ENOENT
)
errmsg
=
"unknown command name in ACL"
;
if
(
errno
==
EINVAL
)
errmsg
=
"syntax error"
;
addReplyErrorFormat
(
c
,
addReplyErrorFormat
(
c
,
"
Syntax e
rror in ACL SETUSER modifier '%s'"
,
"
E
rror in ACL SETUSER modifier '%s'
: %s
"
,
(
char
*
)
c
->
argv
[
j
]
->
ptr
);
(
char
*
)
c
->
argv
[
j
]
->
ptr
,
errmsg
);
return
;
return
;
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment