1 /*
2 * Copyright (c) 2018, Salvatore Sanfilippo <antirez at gmail dot com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Redis nor the names of its contributors may be used
14 * to endorse or promote products derived from this software without
15 * specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "server.h"
31 #include "sha256.h"
32 #include <fcntl.h>
33 #include <ctype.h>
34
35 /* =============================================================================
36 * Global state for ACLs
37 * ==========================================================================*/
38
39 rax *Users; /* Table mapping usernames to user structures. */
40
41 user *DefaultUser; /* Global reference to the default user.
42 Every new connection is associated to it, if no
43 AUTH or HELLO is used to authenticate with a
44 different user. */
45
46 list *UsersToLoad; /* This is a list of users found in the configuration file
47 that we'll need to load in the final stage of Redis
48 initialization, after all the modules are already
49 loaded. Every list element is a NULL terminated
50 array of SDS pointers: the first is the user name,
51 all the remaining pointers are ACL rules in the same
52 format as ACLSetUser(). */
53 list *ACLLog; /* Our security log, the user is able to inspect that
54 using the ACL LOG command .*/
55
56 static rax *commandId = NULL; /* Command name to id mapping */
57
58 static unsigned long nextid = 0; /* Next command id that has not been assigned */
59
60 struct ACLCategoryItem {
61 const char *name;
62 uint64_t flag;
63 } ACLCommandCategories[] = { /* See redis.conf for details on each category. */
64 {"keyspace", CMD_CATEGORY_KEYSPACE},
65 {"read", CMD_CATEGORY_READ},
66 {"write", CMD_CATEGORY_WRITE},
67 {"set", CMD_CATEGORY_SET},
68 {"sortedset", CMD_CATEGORY_SORTEDSET},
69 {"list", CMD_CATEGORY_LIST},
70 {"hash", CMD_CATEGORY_HASH},
71 {"string", CMD_CATEGORY_STRING},
72 {"bitmap", CMD_CATEGORY_BITMAP},
73 {"hyperloglog", CMD_CATEGORY_HYPERLOGLOG},
74 {"geo", CMD_CATEGORY_GEO},
75 {"stream", CMD_CATEGORY_STREAM},
76 {"pubsub", CMD_CATEGORY_PUBSUB},
77 {"admin", CMD_CATEGORY_ADMIN},
78 {"fast", CMD_CATEGORY_FAST},
79 {"slow", CMD_CATEGORY_SLOW},
80 {"blocking", CMD_CATEGORY_BLOCKING},
81 {"dangerous", CMD_CATEGORY_DANGEROUS},
82 {"connection", CMD_CATEGORY_CONNECTION},
83 {"transaction", CMD_CATEGORY_TRANSACTION},
84 {"scripting", CMD_CATEGORY_SCRIPTING},
85 {NULL,0} /* Terminator. */
86 };
87
88 struct ACLUserFlag {
89 const char *name;
90 uint64_t flag;
91 } ACLUserFlags[] = {
92 /* Note: the order here dictates the emitted order at ACLDescribeUser */
93 {"on", USER_FLAG_ENABLED},
94 {"off", USER_FLAG_DISABLED},
95 {"allkeys", USER_FLAG_ALLKEYS},
96 {"allchannels", USER_FLAG_ALLCHANNELS},
97 {"allcommands", USER_FLAG_ALLCOMMANDS},
98 {"nopass", USER_FLAG_NOPASS},
99 {"skip-sanitize-payload", USER_FLAG_SANITIZE_PAYLOAD_SKIP},
100 {"sanitize-payload", USER_FLAG_SANITIZE_PAYLOAD},
101 {NULL,0} /* Terminator. */
102 };
103
104 void ACLResetFirstArgsForCommand(user *u, unsigned long id);
105 void ACLResetFirstArgs(user *u);
106 void ACLAddAllowedFirstArg(user *u, unsigned long id, const char *sub);
107 void ACLFreeLogEntry(void *le);
108
109 /* The length of the string representation of a hashed password. */
110 #define HASH_PASSWORD_LEN SHA256_BLOCK_SIZE*2
111
112 /* =============================================================================
113 * Helper functions for the rest of the ACL implementation
114 * ==========================================================================*/
115
116 /* Return zero if strings are the same, non-zero if they are not.
117 * The comparison is performed in a way that prevents an attacker to obtain
118 * information about the nature of the strings just monitoring the execution
119 * time of the function.
120 *
121 * Note that limiting the comparison length to strings up to 512 bytes we
122 * can avoid leaking any information about the password length and any
123 * possible branch misprediction related leak.
124 */
time_independent_strcmp(char * a,char * b)125 int time_independent_strcmp(char *a, char *b) {
126 char bufa[CONFIG_AUTHPASS_MAX_LEN], bufb[CONFIG_AUTHPASS_MAX_LEN];
127 /* The above two strlen perform len(a) + len(b) operations where either
128 * a or b are fixed (our password) length, and the difference is only
129 * relative to the length of the user provided string, so no information
130 * leak is possible in the following two lines of code. */
131 unsigned int alen = strlen(a);
132 unsigned int blen = strlen(b);
133 unsigned int j;
134 int diff = 0;
135
136 /* We can't compare strings longer than our static buffers.
137 * Note that this will never pass the first test in practical circumstances
138 * so there is no info leak. */
139 if (alen > sizeof(bufa) || blen > sizeof(bufb)) return 1;
140
141 memset(bufa,0,sizeof(bufa)); /* Constant time. */
142 memset(bufb,0,sizeof(bufb)); /* Constant time. */
143 /* Again the time of the following two copies is proportional to
144 * len(a) + len(b) so no info is leaked. */
145 memcpy(bufa,a,alen);
146 memcpy(bufb,b,blen);
147
148 /* Always compare all the chars in the two buffers without
149 * conditional expressions. */
150 for (j = 0; j < sizeof(bufa); j++) {
151 diff |= (bufa[j] ^ bufb[j]);
152 }
153 /* Length must be equal as well. */
154 diff |= alen ^ blen;
155 return diff; /* If zero strings are the same. */
156 }
157
158 /* Given an SDS string, returns the SHA256 hex representation as a
159 * new SDS string. */
ACLHashPassword(unsigned char * cleartext,size_t len)160 sds ACLHashPassword(unsigned char *cleartext, size_t len) {
161 SHA256_CTX ctx;
162 unsigned char hash[SHA256_BLOCK_SIZE];
163 char hex[HASH_PASSWORD_LEN];
164 char *cset = "0123456789abcdef";
165
166 sha256_init(&ctx);
167 sha256_update(&ctx,(unsigned char*)cleartext,len);
168 sha256_final(&ctx,hash);
169
170 for (int j = 0; j < SHA256_BLOCK_SIZE; j++) {
171 hex[j*2] = cset[((hash[j]&0xF0)>>4)];
172 hex[j*2+1] = cset[(hash[j]&0xF)];
173 }
174 return sdsnewlen(hex,HASH_PASSWORD_LEN);
175 }
176
177 /* Given a hash and the hash length, returns C_OK if it is a valid password
178 * hash, or C_ERR otherwise. */
ACLCheckPasswordHash(unsigned char * hash,int hashlen)179 int ACLCheckPasswordHash(unsigned char *hash, int hashlen) {
180 if (hashlen != HASH_PASSWORD_LEN) {
181 return C_ERR;
182 }
183
184 /* Password hashes can only be characters that represent
185 * hexadecimal values, which are numbers and lowercase
186 * characters 'a' through 'f'. */
187 for(int i = 0; i < HASH_PASSWORD_LEN; i++) {
188 char c = hash[i];
189 if ((c < 'a' || c > 'f') && (c < '0' || c > '9')) {
190 return C_ERR;
191 }
192 }
193 return C_OK;
194 }
195
196 /* =============================================================================
197 * Low level ACL API
198 * ==========================================================================*/
199
200 /* Return 1 if the specified string contains spaces or null characters.
201 * We do this for usernames and key patterns for simpler rewriting of
202 * ACL rules, presentation on ACL list, and to avoid subtle security bugs
203 * that may arise from parsing the rules in presence of escapes.
204 * The function returns 0 if the string has no spaces. */
ACLStringHasSpaces(const char * s,size_t len)205 int ACLStringHasSpaces(const char *s, size_t len) {
206 for (size_t i = 0; i < len; i++) {
207 if (isspace(s[i]) || s[i] == 0) return 1;
208 }
209 return 0;
210 }
211
212 /* Given the category name the command returns the corresponding flag, or
213 * zero if there is no match. */
ACLGetCommandCategoryFlagByName(const char * name)214 uint64_t ACLGetCommandCategoryFlagByName(const char *name) {
215 for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {
216 if (!strcasecmp(name,ACLCommandCategories[j].name)) {
217 return ACLCommandCategories[j].flag;
218 }
219 }
220 return 0; /* No match. */
221 }
222
223 /* Method for searching for a user within a list of user definitions. The
224 * list contains an array of user arguments, and we are only
225 * searching the first argument, the username, for a match. */
ACLListMatchLoadedUser(void * definition,void * user)226 int ACLListMatchLoadedUser(void *definition, void *user) {
227 sds *user_definition = definition;
228 return sdscmp(user_definition[0], user) == 0;
229 }
230
231 /* Method for passwords/pattern comparison used for the user->passwords list
232 * so that we can search for items with listSearchKey(). */
ACLListMatchSds(void * a,void * b)233 int ACLListMatchSds(void *a, void *b) {
234 return sdscmp(a,b) == 0;
235 }
236
237 /* Method to free list elements from ACL users password/patterns lists. */
ACLListFreeSds(void * item)238 void ACLListFreeSds(void *item) {
239 sdsfree(item);
240 }
241
242 /* Method to duplicate list elements from ACL users password/patterns lists. */
ACLListDupSds(void * item)243 void *ACLListDupSds(void *item) {
244 return sdsdup(item);
245 }
246
247 /* Create a new user with the specified name, store it in the list
248 * of users (the Users global radix tree), and returns a reference to
249 * the structure representing the user.
250 *
251 * If the user with such name already exists NULL is returned. */
ACLCreateUser(const char * name,size_t namelen)252 user *ACLCreateUser(const char *name, size_t namelen) {
253 if (raxFind(Users,(unsigned char*)name,namelen) != raxNotFound) return NULL;
254 user *u = zmalloc(sizeof(*u));
255 u->name = sdsnewlen(name,namelen);
256 u->flags = USER_FLAG_DISABLED | server.acl_pubsub_default;
257 u->allowed_firstargs = NULL;
258 u->passwords = listCreate();
259 u->patterns = listCreate();
260 u->channels = listCreate();
261 listSetMatchMethod(u->passwords,ACLListMatchSds);
262 listSetFreeMethod(u->passwords,ACLListFreeSds);
263 listSetDupMethod(u->passwords,ACLListDupSds);
264 listSetMatchMethod(u->patterns,ACLListMatchSds);
265 listSetFreeMethod(u->patterns,ACLListFreeSds);
266 listSetDupMethod(u->patterns,ACLListDupSds);
267 listSetMatchMethod(u->channels,ACLListMatchSds);
268 listSetFreeMethod(u->channels,ACLListFreeSds);
269 listSetDupMethod(u->channels,ACLListDupSds);
270 memset(u->allowed_commands,0,sizeof(u->allowed_commands));
271 raxInsert(Users,(unsigned char*)name,namelen,u,NULL);
272 return u;
273 }
274
275 /* This function should be called when we need an unlinked "fake" user
276 * we can use in order to validate ACL rules or for other similar reasons.
277 * The user will not get linked to the Users radix tree. The returned
278 * user should be released with ACLFreeUser() as usually. */
ACLCreateUnlinkedUser(void)279 user *ACLCreateUnlinkedUser(void) {
280 char username[64];
281 for (int j = 0; ; j++) {
282 snprintf(username,sizeof(username),"__fakeuser:%d__",j);
283 user *fakeuser = ACLCreateUser(username,strlen(username));
284 if (fakeuser == NULL) continue;
285 int retval = raxRemove(Users,(unsigned char*) username,
286 strlen(username),NULL);
287 serverAssert(retval != 0);
288 return fakeuser;
289 }
290 }
291
292 /* Release the memory used by the user structure. Note that this function
293 * will not remove the user from the Users global radix tree. */
ACLFreeUser(user * u)294 void ACLFreeUser(user *u) {
295 sdsfree(u->name);
296 listRelease(u->passwords);
297 listRelease(u->patterns);
298 listRelease(u->channels);
299 ACLResetFirstArgs(u);
300 zfree(u);
301 }
302
303 /* When a user is deleted we need to cycle the active
304 * connections in order to kill all the pending ones that
305 * are authenticated with such user. */
ACLFreeUserAndKillClients(user * u)306 void ACLFreeUserAndKillClients(user *u) {
307 listIter li;
308 listNode *ln;
309 listRewind(server.clients,&li);
310 while ((ln = listNext(&li)) != NULL) {
311 client *c = listNodeValue(ln);
312 if (c->user == u) {
313 /* We'll free the connection asynchronously, so
314 * in theory to set a different user is not needed.
315 * However if there are bugs in Redis, soon or later
316 * this may result in some security hole: it's much
317 * more defensive to set the default user and put
318 * it in non authenticated mode. */
319 c->user = DefaultUser;
320 c->authenticated = 0;
321 /* We will write replies to this client later, so we can't
322 * close it directly even if async. */
323 if (c == server.current_client) {
324 c->flags |= CLIENT_CLOSE_AFTER_COMMAND;
325 } else {
326 freeClientAsync(c);
327 }
328 }
329 }
330 ACLFreeUser(u);
331 }
332
333 /* Copy the user ACL rules from the source user 'src' to the destination
334 * user 'dst' so that at the end of the process they'll have exactly the
335 * same rules (but the names will continue to be the original ones). */
ACLCopyUser(user * dst,user * src)336 void ACLCopyUser(user *dst, user *src) {
337 listRelease(dst->passwords);
338 listRelease(dst->patterns);
339 listRelease(dst->channels);
340 dst->passwords = listDup(src->passwords);
341 dst->patterns = listDup(src->patterns);
342 dst->channels = listDup(src->channels);
343 memcpy(dst->allowed_commands,src->allowed_commands,
344 sizeof(dst->allowed_commands));
345 dst->flags = src->flags;
346 ACLResetFirstArgs(dst);
347 /* Copy the allowed first-args array of array of SDS strings. */
348 if (src->allowed_firstargs) {
349 for (int j = 0; j < USER_COMMAND_BITS_COUNT; j++) {
350 if (src->allowed_firstargs[j]) {
351 for (int i = 0; src->allowed_firstargs[j][i]; i++)
352 {
353 ACLAddAllowedFirstArg(dst, j,
354 src->allowed_firstargs[j][i]);
355 }
356 }
357 }
358 }
359 }
360
361 /* Free all the users registered in the radix tree 'users' and free the
362 * radix tree itself. */
ACLFreeUsersSet(rax * users)363 void ACLFreeUsersSet(rax *users) {
364 raxFreeWithCallback(users,(void(*)(void*))ACLFreeUserAndKillClients);
365 }
366
367 /* Given a command ID, this function set by reference 'word' and 'bit'
368 * so that user->allowed_commands[word] will address the right word
369 * where the corresponding bit for the provided ID is stored, and
370 * so that user->allowed_commands[word]&bit will identify that specific
371 * bit. The function returns C_ERR in case the specified ID overflows
372 * the bitmap in the user representation. */
ACLGetCommandBitCoordinates(uint64_t id,uint64_t * word,uint64_t * bit)373 int ACLGetCommandBitCoordinates(uint64_t id, uint64_t *word, uint64_t *bit) {
374 if (id >= USER_COMMAND_BITS_COUNT) return C_ERR;
375 *word = id / sizeof(uint64_t) / 8;
376 *bit = 1ULL << (id % (sizeof(uint64_t) * 8));
377 return C_OK;
378 }
379
380 /* Check if the specified command bit is set for the specified user.
381 * The function returns 1 is the bit is set or 0 if it is not.
382 * Note that this function does not check the ALLCOMMANDS flag of the user
383 * but just the lowlevel bitmask.
384 *
385 * If the bit overflows the user internal representation, zero is returned
386 * in order to disallow the execution of the command in such edge case. */
ACLGetUserCommandBit(const user * u,unsigned long id)387 int ACLGetUserCommandBit(const user *u, unsigned long id) {
388 uint64_t word, bit;
389 if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return 0;
390 return (u->allowed_commands[word] & bit) != 0;
391 }
392
393 /* When +@all or allcommands is given, we set a reserved bit as well that we
394 * can later test, to see if the user has the right to execute "future commands",
395 * that is, commands loaded later via modules. */
ACLUserCanExecuteFutureCommands(user * u)396 int ACLUserCanExecuteFutureCommands(user *u) {
397 return ACLGetUserCommandBit(u,USER_COMMAND_BITS_COUNT-1);
398 }
399
400 /* Set the specified command bit for the specified user to 'value' (0 or 1).
401 * If the bit overflows the user internal representation, no operation
402 * is performed. As a side effect of calling this function with a value of
403 * zero, the user flag ALLCOMMANDS is cleared since it is no longer possible
404 * to skip the command bit explicit test. */
ACLSetUserCommandBit(user * u,unsigned long id,int value)405 void ACLSetUserCommandBit(user *u, unsigned long id, int value) {
406 uint64_t word, bit;
407 if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return;
408 if (value) {
409 u->allowed_commands[word] |= bit;
410 } else {
411 u->allowed_commands[word] &= ~bit;
412 u->flags &= ~USER_FLAG_ALLCOMMANDS;
413 }
414 }
415
416 /* This function is used to allow/block a specific command.
417 * Allowing/blocking a container command also applies for its subcommands */
ACLChangeCommandPerm(user * u,struct redisCommand * cmd,int allow)418 void ACLChangeCommandPerm(user *u, struct redisCommand *cmd, int allow) {
419 unsigned long id = cmd->id;
420 ACLSetUserCommandBit(u,id,allow);
421 ACLResetFirstArgsForCommand(u,id);
422 if (cmd->subcommands_dict) {
423 dictEntry *de;
424 dictIterator *di = dictGetSafeIterator(cmd->subcommands_dict);
425 while((de = dictNext(di)) != NULL) {
426 struct redisCommand *sub = (struct redisCommand *)dictGetVal(de);
427 ACLSetUserCommandBit(u,sub->id,allow);
428 }
429 dictReleaseIterator(di);
430 }
431 }
432
ACLSetUserCommandBitsForCategoryLogic(dict * commands,user * u,uint64_t cflag,int value)433 void ACLSetUserCommandBitsForCategoryLogic(dict *commands, user *u, uint64_t cflag, int value) {
434 dictIterator *di = dictGetIterator(commands);
435 dictEntry *de;
436 while ((de = dictNext(di)) != NULL) {
437 struct redisCommand *cmd = dictGetVal(de);
438 if (cmd->flags & CMD_MODULE) continue; /* Ignore modules commands. */
439 if (cmd->flags & cflag) {
440 ACLChangeCommandPerm(u,cmd,value);
441 }
442 if (cmd->subcommands_dict) {
443 ACLSetUserCommandBitsForCategoryLogic(cmd->subcommands_dict, u, cflag, value);
444 }
445 }
446 dictReleaseIterator(di);
447 }
448
449 /* This is like ACLSetUserCommandBit(), but instead of setting the specified
450 * ID, it will check all the commands in the category specified as argument,
451 * and will set all the bits corresponding to such commands to the specified
452 * value. Since the category passed by the user may be non existing, the
453 * function returns C_ERR if the category was not found, or C_OK if it was
454 * found and the operation was performed. */
ACLSetUserCommandBitsForCategory(user * u,const char * category,int value)455 int ACLSetUserCommandBitsForCategory(user *u, const char *category, int value) {
456 uint64_t cflag = ACLGetCommandCategoryFlagByName(category);
457 if (!cflag) return C_ERR;
458 ACLSetUserCommandBitsForCategoryLogic(server.orig_commands, u, cflag, value);
459 return C_OK;
460 }
461
ACLCountCategoryBitsForCommands(dict * commands,user * u,unsigned long * on,unsigned long * off,uint64_t cflag)462 void ACLCountCategoryBitsForCommands(dict *commands, user *u, unsigned long *on, unsigned long *off, uint64_t cflag) {
463 dictIterator *di = dictGetIterator(commands);
464 dictEntry *de;
465 while ((de = dictNext(di)) != NULL) {
466 struct redisCommand *cmd = dictGetVal(de);
467 if (cmd->flags & cflag) {
468 if (ACLGetUserCommandBit(u,cmd->id))
469 (*on)++;
470 else
471 (*off)++;
472 }
473 if (cmd->subcommands_dict) {
474 ACLCountCategoryBitsForCommands(cmd->subcommands_dict, u, on, off, cflag);
475 }
476 }
477 dictReleaseIterator(di);
478 }
479
480 /* Return the number of commands allowed (on) and denied (off) for the user 'u'
481 * in the subset of commands flagged with the specified category name.
482 * If the category name is not valid, C_ERR is returned, otherwise C_OK is
483 * returned and on and off are populated by reference. */
ACLCountCategoryBitsForUser(user * u,unsigned long * on,unsigned long * off,const char * category)484 int ACLCountCategoryBitsForUser(user *u, unsigned long *on, unsigned long *off,
485 const char *category)
486 {
487 uint64_t cflag = ACLGetCommandCategoryFlagByName(category);
488 if (!cflag) return C_ERR;
489
490 *on = *off = 0;
491 ACLCountCategoryBitsForCommands(server.orig_commands, u, on, off, cflag);
492 return C_OK;
493 }
494
ACLDescribeUserCommandRulesSingleCommands(user * u,user * fakeuser,sds rules,dict * commands)495 sds ACLDescribeUserCommandRulesSingleCommands(user *u, user *fakeuser, sds rules, dict *commands) {
496 dictIterator *di = dictGetIterator(commands);
497 dictEntry *de;
498 while ((de = dictNext(di)) != NULL) {
499 struct redisCommand *cmd = dictGetVal(de);
500 int userbit = ACLGetUserCommandBit(u,cmd->id);
501 int fakebit = ACLGetUserCommandBit(fakeuser,cmd->id);
502 if (userbit != fakebit) {
503 rules = sdscatlen(rules, userbit ? "+" : "-", 1);
504 sds fullname = getFullCommandName(cmd);
505 rules = sdscat(rules,fullname);
506 sdsfree(fullname);
507 rules = sdscatlen(rules," ",1);
508 ACLChangeCommandPerm(fakeuser,cmd,userbit);
509 }
510
511 if (cmd->subcommands_dict)
512 rules = ACLDescribeUserCommandRulesSingleCommands(u,fakeuser,rules,cmd->subcommands_dict);
513
514 /* Emit the first-args if there are any. */
515 if (userbit == 0 && u->allowed_firstargs &&
516 u->allowed_firstargs[cmd->id])
517 {
518 for (int j = 0; u->allowed_firstargs[cmd->id][j]; j++) {
519 rules = sdscatlen(rules,"+",1);
520 sds fullname = getFullCommandName(cmd);
521 rules = sdscat(rules,fullname);
522 sdsfree(fullname);
523 rules = sdscatlen(rules,"|",1);
524 rules = sdscatsds(rules,u->allowed_firstargs[cmd->id][j]);
525 rules = sdscatlen(rules," ",1);
526 }
527 }
528 }
529 dictReleaseIterator(di);
530 return rules;
531 }
532
533 /* This function returns an SDS string representing the specified user ACL
534 * rules related to command execution, in the same format you could set them
535 * back using ACL SETUSER. The function will return just the set of rules needed
536 * to recreate the user commands bitmap, without including other user flags such
537 * as on/off, passwords and so forth. The returned string always starts with
538 * the +@all or -@all rule, depending on the user bitmap, and is followed, if
539 * needed, by the other rules needed to narrow or extend what the user can do. */
ACLDescribeUserCommandRules(user * u)540 sds ACLDescribeUserCommandRules(user *u) {
541 sds rules = sdsempty();
542 int additive; /* If true we start from -@all and add, otherwise if
543 false we start from +@all and remove. */
544
545 /* This code is based on a trick: as we generate the rules, we apply
546 * them to a fake user, so that as we go we still know what are the
547 * bit differences we should try to address by emitting more rules. */
548 user fu = {0};
549 user *fakeuser = &fu;
550
551 /* Here we want to understand if we should start with +@all and remove
552 * the commands corresponding to the bits that are not set in the user
553 * commands bitmap, or the contrary. Note that semantically the two are
554 * different. For instance starting with +@all and subtracting, the user
555 * will be able to execute future commands, while -@all and adding will just
556 * allow the user the run the selected commands and/or categories.
557 * How do we test for that? We use the trick of a reserved command ID bit
558 * that is set only by +@all (and its alias "allcommands"). */
559 if (ACLUserCanExecuteFutureCommands(u)) {
560 additive = 0;
561 rules = sdscat(rules,"+@all ");
562 ACLSetUser(fakeuser,"+@all",-1);
563 } else {
564 additive = 1;
565 rules = sdscat(rules,"-@all ");
566 ACLSetUser(fakeuser,"-@all",-1);
567 }
568
569 /* Attempt to find a good approximation for categories and commands
570 * based on the current bits used, by looping over the category list
571 * and applying the best fit each time. Often a set of categories will not
572 * perfectly match the set of commands into it, so at the end we do a
573 * final pass adding/removing the single commands needed to make the bitmap
574 * exactly match. A temp user is maintained to keep track of categories
575 * already applied. */
576 user tu = {0};
577 user *tempuser = &tu;
578
579 /* Keep track of the categories that have been applied, to prevent
580 * applying them twice. */
581 char applied[sizeof(ACLCommandCategories)/sizeof(ACLCommandCategories[0])];
582 memset(applied, 0, sizeof(applied));
583
584 memcpy(tempuser->allowed_commands,
585 u->allowed_commands,
586 sizeof(u->allowed_commands));
587 while (1) {
588 int best = -1;
589 unsigned long mindiff = INT_MAX, maxsame = 0;
590 for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {
591 if (applied[j]) continue;
592
593 unsigned long on, off, diff, same;
594 ACLCountCategoryBitsForUser(tempuser,&on,&off,ACLCommandCategories[j].name);
595 /* Check if the current category is the best this loop:
596 * * It has more commands in common with the user than commands
597 * that are different.
598 * AND EITHER
599 * * It has the fewest number of differences
600 * than the best match we have found so far.
601 * * OR it matches the fewest number of differences
602 * that we've seen but it has more in common. */
603 diff = additive ? off : on;
604 same = additive ? on : off;
605 if (same > diff &&
606 ((diff < mindiff) || (diff == mindiff && same > maxsame)))
607 {
608 best = j;
609 mindiff = diff;
610 maxsame = same;
611 }
612 }
613
614 /* We didn't find a match */
615 if (best == -1) break;
616
617 sds op = sdsnewlen(additive ? "+@" : "-@", 2);
618 op = sdscat(op,ACLCommandCategories[best].name);
619 ACLSetUser(fakeuser,op,-1);
620
621 sds invop = sdsnewlen(additive ? "-@" : "+@", 2);
622 invop = sdscat(invop,ACLCommandCategories[best].name);
623 ACLSetUser(tempuser,invop,-1);
624
625 rules = sdscatsds(rules,op);
626 rules = sdscatlen(rules," ",1);
627 sdsfree(op);
628 sdsfree(invop);
629
630 applied[best] = 1;
631 }
632
633 /* Fix the final ACLs with single commands differences. */
634 rules = ACLDescribeUserCommandRulesSingleCommands(u,fakeuser,rules,server.orig_commands);
635
636 /* Trim the final useless space. */
637 sdsrange(rules,0,-2);
638
639 /* This is technically not needed, but we want to verify that now the
640 * predicted bitmap is exactly the same as the user bitmap, and abort
641 * otherwise, because aborting is better than a security risk in this
642 * code path. */
643 if (memcmp(fakeuser->allowed_commands,
644 u->allowed_commands,
645 sizeof(u->allowed_commands)) != 0)
646 {
647 serverLog(LL_WARNING,
648 "CRITICAL ERROR: User ACLs don't match final bitmap: '%s'",
649 rules);
650 serverPanic("No bitmap match in ACLDescribeUserCommandRules()");
651 }
652 return rules;
653 }
654
655 /* This is similar to ACLDescribeUserCommandRules(), however instead of
656 * describing just the user command rules, everything is described: user
657 * flags, keys, passwords and finally the command rules obtained via
658 * the ACLDescribeUserCommandRules() function. This is the function we call
659 * when we want to rewrite the configuration files describing ACLs and
660 * in order to show users with ACL LIST. */
ACLDescribeUser(user * u)661 sds ACLDescribeUser(user *u) {
662 sds res = sdsempty();
663
664 /* Flags. */
665 for (int j = 0; ACLUserFlags[j].flag; j++) {
666 /* Skip the allcommands, allkeys and allchannels flags because they'll
667 * be emitted later as +@all, ~* and &*. */
668 if (ACLUserFlags[j].flag == USER_FLAG_ALLKEYS ||
669 ACLUserFlags[j].flag == USER_FLAG_ALLCHANNELS ||
670 ACLUserFlags[j].flag == USER_FLAG_ALLCOMMANDS) continue;
671 if (u->flags & ACLUserFlags[j].flag) {
672 res = sdscat(res,ACLUserFlags[j].name);
673 res = sdscatlen(res," ",1);
674 }
675 }
676
677 /* Passwords. */
678 listIter li;
679 listNode *ln;
680 listRewind(u->passwords,&li);
681 while((ln = listNext(&li))) {
682 sds thispass = listNodeValue(ln);
683 res = sdscatlen(res,"#",1);
684 res = sdscatsds(res,thispass);
685 res = sdscatlen(res," ",1);
686 }
687
688 /* Key patterns. */
689 if (u->flags & USER_FLAG_ALLKEYS) {
690 res = sdscatlen(res,"~* ",3);
691 } else {
692 listRewind(u->patterns,&li);
693 while((ln = listNext(&li))) {
694 sds thispat = listNodeValue(ln);
695 res = sdscatlen(res,"~",1);
696 res = sdscatsds(res,thispat);
697 res = sdscatlen(res," ",1);
698 }
699 }
700
701 /* Pub/sub channel patterns. */
702 if (u->flags & USER_FLAG_ALLCHANNELS) {
703 res = sdscatlen(res,"&* ",3);
704 } else {
705 res = sdscatlen(res,"resetchannels ",14);
706 listRewind(u->channels,&li);
707 while((ln = listNext(&li))) {
708 sds thispat = listNodeValue(ln);
709 res = sdscatlen(res,"&",1);
710 res = sdscatsds(res,thispat);
711 res = sdscatlen(res," ",1);
712 }
713 }
714
715 /* Command rules. */
716 sds rules = ACLDescribeUserCommandRules(u);
717 res = sdscatsds(res,rules);
718 sdsfree(rules);
719 return res;
720 }
721
722 /* Get a command from the original command table, that is not affected
723 * by the command renaming operations: we base all the ACL work from that
724 * table, so that ACLs are valid regardless of command renaming. */
ACLLookupCommand(const char * name)725 struct redisCommand *ACLLookupCommand(const char *name) {
726 struct redisCommand *cmd;
727 sds sdsname = sdsnew(name);
728 cmd = lookupCommandBySdsLogic(server.orig_commands,sdsname);
729 sdsfree(sdsname);
730 return cmd;
731 }
732
733 /* Flush the array of allowed first-args for the specified user
734 * and command ID. */
ACLResetFirstArgsForCommand(user * u,unsigned long id)735 void ACLResetFirstArgsForCommand(user *u, unsigned long id) {
736 if (u->allowed_firstargs && u->allowed_firstargs[id]) {
737 for (int i = 0; u->allowed_firstargs[id][i]; i++)
738 sdsfree(u->allowed_firstargs[id][i]);
739 zfree(u->allowed_firstargs[id]);
740 u->allowed_firstargs[id] = NULL;
741 }
742 }
743
744 /* Flush the entire table of first-args. This is useful on +@all, -@all
745 * or similar to return back to the minimal memory usage (and checks to do)
746 * for the user. */
ACLResetFirstArgs(user * u)747 void ACLResetFirstArgs(user *u) {
748 if (u->allowed_firstargs == NULL) return;
749 for (int j = 0; j < USER_COMMAND_BITS_COUNT; j++) {
750 if (u->allowed_firstargs[j]) {
751 for (int i = 0; u->allowed_firstargs[j][i]; i++)
752 sdsfree(u->allowed_firstargs[j][i]);
753 zfree(u->allowed_firstargs[j]);
754 }
755 }
756 zfree(u->allowed_firstargs);
757 u->allowed_firstargs = NULL;
758 }
759
760 /* Add a first-arh to the list of subcommands for the user 'u' and
761 * the command id specified. */
ACLAddAllowedFirstArg(user * u,unsigned long id,const char * sub)762 void ACLAddAllowedFirstArg(user *u, unsigned long id, const char *sub) {
763 /* If this is the first first-arg to be configured for
764 * this user, we have to allocate the first-args array. */
765 if (u->allowed_firstargs == NULL) {
766 u->allowed_firstargs = zcalloc(USER_COMMAND_BITS_COUNT * sizeof(sds*));
767 }
768
769 /* We also need to enlarge the allocation pointing to the
770 * null terminated SDS array, to make space for this one.
771 * To start check the current size, and while we are here
772 * make sure the first-arg is not already specified inside. */
773 long items = 0;
774 if (u->allowed_firstargs[id]) {
775 while(u->allowed_firstargs[id][items]) {
776 /* If it's already here do not add it again. */
777 if (!strcasecmp(u->allowed_firstargs[id][items],sub))
778 return;
779 items++;
780 }
781 }
782
783 /* Now we can make space for the new item (and the null term). */
784 items += 2;
785 u->allowed_firstargs[id] = zrealloc(u->allowed_firstargs[id], sizeof(sds)*items);
786 u->allowed_firstargs[id][items-2] = sdsnew(sub);
787 u->allowed_firstargs[id][items-1] = NULL;
788 }
789
790 /* Set user properties according to the string "op". The following
791 * is a description of what different strings will do:
792 *
793 * on Enable the user: it is possible to authenticate as this user.
794 * off Disable the user: it's no longer possible to authenticate
795 * with this user, however the already authenticated connections
796 * will still work.
797 * +<command> Allow the execution of that command.
798 * May be used with `|` for allowing subcommands (e.g "+config|get")
799 * -<command> Disallow the execution of that command.
800 * May be used with `|` for blocking subcommands (e.g "-config|set")
801 * +@<category> Allow the execution of all the commands in such category
802 * with valid categories are like @admin, @set, @sortedset, ...
803 * and so forth, see the full list in the server.c file where
804 * the Redis command table is described and defined.
805 * The special category @all means all the commands, but currently
806 * present in the server, and that will be loaded in the future
807 * via modules.
808 * +<command>|first-arg Allow a specific first argument of an otherwise
809 * disabled command. Note that this form is not
810 * allowed as negative like -SELECT|1, but
811 * only additive starting with "+".
812 * allcommands Alias for +@all. Note that it implies the ability to execute
813 * all the future commands loaded via the modules system.
814 * nocommands Alias for -@all.
815 * ~<pattern> Add a pattern of keys that can be mentioned as part of
816 * commands. For instance ~* allows all the keys. The pattern
817 * is a glob-style pattern like the one of KEYS.
818 * It is possible to specify multiple patterns.
819 * allkeys Alias for ~*
820 * resetkeys Flush the list of allowed keys patterns.
821 * &<pattern> Add a pattern of channels that can be mentioned as part of
822 * Pub/Sub commands. For instance &* allows all the channels. The
823 * pattern is a glob-style pattern like the one of PSUBSCRIBE.
824 * It is possible to specify multiple patterns.
825 * allchannels Alias for &*
826 * resetchannels Flush the list of allowed keys patterns.
827 * ><password> Add this password to the list of valid password for the user.
828 * For example >mypass will add "mypass" to the list.
829 * This directive clears the "nopass" flag (see later).
830 * #<hash> Add this password hash to the list of valid hashes for
831 * the user. This is useful if you have previously computed
832 * the hash, and don't want to store it in plaintext.
833 * This directive clears the "nopass" flag (see later).
834 * <<password> Remove this password from the list of valid passwords.
835 * !<hash> Remove this hashed password from the list of valid passwords.
836 * This is useful when you want to remove a password just by
837 * hash without knowing its plaintext version at all.
838 * nopass All the set passwords of the user are removed, and the user
839 * is flagged as requiring no password: it means that every
840 * password will work against this user. If this directive is
841 * used for the default user, every new connection will be
842 * immediately authenticated with the default user without
843 * any explicit AUTH command required. Note that the "resetpass"
844 * directive will clear this condition.
845 * resetpass Flush the list of allowed passwords. Moreover removes the
846 * "nopass" status. After "resetpass" the user has no associated
847 * passwords and there is no way to authenticate without adding
848 * some password (or setting it as "nopass" later).
849 * reset Performs the following actions: resetpass, resetkeys, off,
850 * -@all. The user returns to the same state it has immediately
851 * after its creation.
852 *
853 * The 'op' string must be null terminated. The 'oplen' argument should
854 * specify the length of the 'op' string in case the caller requires to pass
855 * binary data (for instance the >password form may use a binary password).
856 * Otherwise the field can be set to -1 and the function will use strlen()
857 * to determine the length.
858 *
859 * The function returns C_OK if the action to perform was understood because
860 * the 'op' string made sense. Otherwise C_ERR is returned if the operation
861 * is unknown or has some syntax error.
862 *
863 * When an error is returned, errno is set to the following values:
864 *
865 * EINVAL: The specified opcode is not understood or the key/channel pattern is
866 * invalid (contains non allowed characters).
867 * ENOENT: The command name or command category provided with + or - is not
868 * known.
869 * EEXIST: You are adding a key pattern after "*" was already added. This is
870 * almost surely an error on the user side.
871 * EISDIR: You are adding a channel pattern after "*" was already added. This is
872 * almost surely an error on the user side.
873 * ENODEV: The password you are trying to remove from the user does not exist.
874 * EBADMSG: The hash you are trying to add is not a valid hash.
875 */
ACLSetUser(user * u,const char * op,ssize_t oplen)876 int ACLSetUser(user *u, const char *op, ssize_t oplen) {
877 if (oplen == -1) oplen = strlen(op);
878 if (oplen == 0) return C_OK; /* Empty string is a no-operation. */
879 if (!strcasecmp(op,"on")) {
880 u->flags |= USER_FLAG_ENABLED;
881 u->flags &= ~USER_FLAG_DISABLED;
882 } else if (!strcasecmp(op,"off")) {
883 u->flags |= USER_FLAG_DISABLED;
884 u->flags &= ~USER_FLAG_ENABLED;
885 } else if (!strcasecmp(op,"skip-sanitize-payload")) {
886 u->flags |= USER_FLAG_SANITIZE_PAYLOAD_SKIP;
887 u->flags &= ~USER_FLAG_SANITIZE_PAYLOAD;
888 } else if (!strcasecmp(op,"sanitize-payload")) {
889 u->flags &= ~USER_FLAG_SANITIZE_PAYLOAD_SKIP;
890 u->flags |= USER_FLAG_SANITIZE_PAYLOAD;
891 } else if (!strcasecmp(op,"allkeys") ||
892 !strcasecmp(op,"~*"))
893 {
894 u->flags |= USER_FLAG_ALLKEYS;
895 listEmpty(u->patterns);
896 } else if (!strcasecmp(op,"resetkeys")) {
897 u->flags &= ~USER_FLAG_ALLKEYS;
898 listEmpty(u->patterns);
899 } else if (!strcasecmp(op,"allchannels") ||
900 !strcasecmp(op,"&*"))
901 {
902 u->flags |= USER_FLAG_ALLCHANNELS;
903 listEmpty(u->channels);
904 } else if (!strcasecmp(op,"resetchannels")) {
905 u->flags &= ~USER_FLAG_ALLCHANNELS;
906 listEmpty(u->channels);
907 } else if (!strcasecmp(op,"allcommands") ||
908 !strcasecmp(op,"+@all"))
909 {
910 memset(u->allowed_commands,255,sizeof(u->allowed_commands));
911 u->flags |= USER_FLAG_ALLCOMMANDS;
912 ACLResetFirstArgs(u);
913 } else if (!strcasecmp(op,"nocommands") ||
914 !strcasecmp(op,"-@all"))
915 {
916 memset(u->allowed_commands,0,sizeof(u->allowed_commands));
917 u->flags &= ~USER_FLAG_ALLCOMMANDS;
918 ACLResetFirstArgs(u);
919 } else if (!strcasecmp(op,"nopass")) {
920 u->flags |= USER_FLAG_NOPASS;
921 listEmpty(u->passwords);
922 } else if (!strcasecmp(op,"resetpass")) {
923 u->flags &= ~USER_FLAG_NOPASS;
924 listEmpty(u->passwords);
925 } else if (op[0] == '>' || op[0] == '#') {
926 sds newpass;
927 if (op[0] == '>') {
928 newpass = ACLHashPassword((unsigned char*)op+1,oplen-1);
929 } else {
930 if (ACLCheckPasswordHash((unsigned char*)op+1,oplen-1) == C_ERR) {
931 errno = EBADMSG;
932 return C_ERR;
933 }
934 newpass = sdsnewlen(op+1,oplen-1);
935 }
936
937 listNode *ln = listSearchKey(u->passwords,newpass);
938 /* Avoid re-adding the same password multiple times. */
939 if (ln == NULL)
940 listAddNodeTail(u->passwords,newpass);
941 else
942 sdsfree(newpass);
943 u->flags &= ~USER_FLAG_NOPASS;
944 } else if (op[0] == '<' || op[0] == '!') {
945 sds delpass;
946 if (op[0] == '<') {
947 delpass = ACLHashPassword((unsigned char*)op+1,oplen-1);
948 } else {
949 if (ACLCheckPasswordHash((unsigned char*)op+1,oplen-1) == C_ERR) {
950 errno = EBADMSG;
951 return C_ERR;
952 }
953 delpass = sdsnewlen(op+1,oplen-1);
954 }
955 listNode *ln = listSearchKey(u->passwords,delpass);
956 sdsfree(delpass);
957 if (ln) {
958 listDelNode(u->passwords,ln);
959 } else {
960 errno = ENODEV;
961 return C_ERR;
962 }
963 } else if (op[0] == '~') {
964 if (u->flags & USER_FLAG_ALLKEYS) {
965 errno = EEXIST;
966 return C_ERR;
967 }
968 if (ACLStringHasSpaces(op+1,oplen-1)) {
969 errno = EINVAL;
970 return C_ERR;
971 }
972 sds newpat = sdsnewlen(op+1,oplen-1);
973 listNode *ln = listSearchKey(u->patterns,newpat);
974 /* Avoid re-adding the same key pattern multiple times. */
975 if (ln == NULL)
976 listAddNodeTail(u->patterns,newpat);
977 else
978 sdsfree(newpat);
979 u->flags &= ~USER_FLAG_ALLKEYS;
980 } else if (op[0] == '&') {
981 if (u->flags & USER_FLAG_ALLCHANNELS) {
982 errno = EISDIR;
983 return C_ERR;
984 }
985 if (ACLStringHasSpaces(op+1,oplen-1)) {
986 errno = EINVAL;
987 return C_ERR;
988 }
989 sds newpat = sdsnewlen(op+1,oplen-1);
990 listNode *ln = listSearchKey(u->channels,newpat);
991 /* Avoid re-adding the same channel pattern multiple times. */
992 if (ln == NULL)
993 listAddNodeTail(u->channels,newpat);
994 else
995 sdsfree(newpat);
996 u->flags &= ~USER_FLAG_ALLCHANNELS;
997 } else if (op[0] == '+' && op[1] != '@') {
998 if (strrchr(op,'|') == NULL) {
999 struct redisCommand *cmd = ACLLookupCommand(op+1);
1000 if (cmd == NULL) {
1001 errno = ENOENT;
1002 return C_ERR;
1003 }
1004 ACLChangeCommandPerm(u,cmd,1);
1005 } else {
1006 /* Split the command and subcommand parts. */
1007 char *copy = zstrdup(op+1);
1008 char *sub = strrchr(copy,'|');
1009 sub[0] = '\0';
1010 sub++;
1011
1012 struct redisCommand *cmd = ACLLookupCommand(copy);
1013
1014 /* Check if the command exists. We can't check the
1015 * subcommand to see if it is valid. */
1016 if (cmd == NULL) {
1017 zfree(copy);
1018 errno = ENOENT;
1019 return C_ERR;
1020 }
1021
1022 /* The subcommand cannot be empty, so things like DEBUG|
1023 * are syntax errors of course. */
1024 if (strlen(sub) == 0) {
1025 zfree(copy);
1026 errno = EINVAL;
1027 return C_ERR;
1028 }
1029
1030 if (cmd->subcommands_dict) {
1031 /* If user is trying to allow a valid subcommand we can just add its unique ID */
1032 struct redisCommand *cmd = ACLLookupCommand(op+1);
1033 if (cmd == NULL) {
1034 zfree(copy);
1035 errno = ENOENT;
1036 return C_ERR;
1037 }
1038 ACLChangeCommandPerm(u,cmd,1);
1039 } else {
1040 /* If user is trying to use the ACL mech to block SELECT except SELECT 0 or
1041 * block DEBUG except DEBUG OBJECT (DEBUG subcommands are not considered
1042 * subcommands for now) we use the allowed_firstargs mechanism. */
1043 struct redisCommand *cmd = ACLLookupCommand(copy);
1044 if (cmd == NULL) {
1045 zfree(copy);
1046 errno = ENOENT;
1047 return C_ERR;
1048 }
1049 /* Add the first-arg to the list of valid ones. */
1050 ACLAddAllowedFirstArg(u,cmd->id,sub);
1051 }
1052
1053 zfree(copy);
1054 }
1055 } else if (op[0] == '-' && op[1] != '@') {
1056 struct redisCommand *cmd = ACLLookupCommand(op+1);
1057 if (cmd == NULL) {
1058 errno = ENOENT;
1059 return C_ERR;
1060 }
1061 ACLChangeCommandPerm(u,cmd,0);
1062 } else if ((op[0] == '+' || op[0] == '-') && op[1] == '@') {
1063 int bitval = op[0] == '+' ? 1 : 0;
1064 if (ACLSetUserCommandBitsForCategory(u,op+2,bitval) == C_ERR) {
1065 errno = ENOENT;
1066 return C_ERR;
1067 }
1068 } else if (!strcasecmp(op,"reset")) {
1069 serverAssert(ACLSetUser(u,"resetpass",-1) == C_OK);
1070 serverAssert(ACLSetUser(u,"resetkeys",-1) == C_OK);
1071 serverAssert(ACLSetUser(u,"resetchannels",-1) == C_OK);
1072 if (server.acl_pubsub_default & USER_FLAG_ALLCHANNELS)
1073 serverAssert(ACLSetUser(u,"allchannels",-1) == C_OK);
1074 serverAssert(ACLSetUser(u,"off",-1) == C_OK);
1075 serverAssert(ACLSetUser(u,"sanitize-payload",-1) == C_OK);
1076 serverAssert(ACLSetUser(u,"-@all",-1) == C_OK);
1077 } else {
1078 errno = EINVAL;
1079 return C_ERR;
1080 }
1081 return C_OK;
1082 }
1083
1084 /* Return a description of the error that occurred in ACLSetUser() according to
1085 * the errno value set by the function on error. */
ACLSetUserStringError(void)1086 const char *ACLSetUserStringError(void) {
1087 const char *errmsg = "Wrong format";
1088 if (errno == ENOENT)
1089 errmsg = "Unknown command or category name in ACL";
1090 else if (errno == EINVAL)
1091 errmsg = "Syntax error";
1092 else if (errno == EEXIST)
1093 errmsg = "Adding a pattern after the * pattern (or the "
1094 "'allkeys' flag) is not valid and does not have any "
1095 "effect. Try 'resetkeys' to start with an empty "
1096 "list of patterns";
1097 else if (errno == EISDIR)
1098 errmsg = "Adding a pattern after the * pattern (or the "
1099 "'allchannels' flag) is not valid and does not have any "
1100 "effect. Try 'resetchannels' to start with an empty "
1101 "list of channels";
1102 else if (errno == ENODEV)
1103 errmsg = "The password you are trying to remove from the user does "
1104 "not exist";
1105 else if (errno == EBADMSG)
1106 errmsg = "The password hash must be exactly 64 characters and contain "
1107 "only lowercase hexadecimal characters";
1108 else if (errno == EALREADY)
1109 errmsg = "Duplicate user found. A user can only be defined once in "
1110 "config files";
1111 return errmsg;
1112 }
1113
1114 /* Create the default user, this has special permissions. */
ACLCreateDefaultUser(void)1115 user *ACLCreateDefaultUser(void) {
1116 user *new = ACLCreateUser("default",7);
1117 ACLSetUser(new,"+@all",-1);
1118 ACLSetUser(new,"~*",-1);
1119 ACLSetUser(new,"&*",-1);
1120 ACLSetUser(new,"on",-1);
1121 ACLSetUser(new,"nopass",-1);
1122 return new;
1123 }
1124
1125 /* Initialization of the ACL subsystem. */
ACLInit(void)1126 void ACLInit(void) {
1127 Users = raxNew();
1128 UsersToLoad = listCreate();
1129 listSetMatchMethod(UsersToLoad, ACLListMatchLoadedUser);
1130 ACLLog = listCreate();
1131 DefaultUser = ACLCreateDefaultUser();
1132 }
1133
1134 /* Check the username and password pair and return C_OK if they are valid,
1135 * otherwise C_ERR is returned and errno is set to:
1136 *
1137 * EINVAL: if the username-password do not match.
1138 * ENONENT: if the specified user does not exist at all.
1139 */
ACLCheckUserCredentials(robj * username,robj * password)1140 int ACLCheckUserCredentials(robj *username, robj *password) {
1141 user *u = ACLGetUserByName(username->ptr,sdslen(username->ptr));
1142 if (u == NULL) {
1143 errno = ENOENT;
1144 return C_ERR;
1145 }
1146
1147 /* Disabled users can't login. */
1148 if (u->flags & USER_FLAG_DISABLED) {
1149 errno = EINVAL;
1150 return C_ERR;
1151 }
1152
1153 /* If the user is configured to don't require any password, we
1154 * are already fine here. */
1155 if (u->flags & USER_FLAG_NOPASS) return C_OK;
1156
1157 /* Check all the user passwords for at least one to match. */
1158 listIter li;
1159 listNode *ln;
1160 listRewind(u->passwords,&li);
1161 sds hashed = ACLHashPassword(password->ptr,sdslen(password->ptr));
1162 while((ln = listNext(&li))) {
1163 sds thispass = listNodeValue(ln);
1164 if (!time_independent_strcmp(hashed, thispass)) {
1165 sdsfree(hashed);
1166 return C_OK;
1167 }
1168 }
1169 sdsfree(hashed);
1170
1171 /* If we reached this point, no password matched. */
1172 errno = EINVAL;
1173 return C_ERR;
1174 }
1175
1176 /* This is like ACLCheckUserCredentials(), however if the user/pass
1177 * are correct, the connection is put in authenticated state and the
1178 * connection user reference is populated.
1179 *
1180 * The return value is C_OK or C_ERR with the same meaning as
1181 * ACLCheckUserCredentials(). */
ACLAuthenticateUser(client * c,robj * username,robj * password)1182 int ACLAuthenticateUser(client *c, robj *username, robj *password) {
1183 if (ACLCheckUserCredentials(username,password) == C_OK) {
1184 c->authenticated = 1;
1185 c->user = ACLGetUserByName(username->ptr,sdslen(username->ptr));
1186 moduleNotifyUserChanged(c);
1187 return C_OK;
1188 } else {
1189 addACLLogEntry(c,ACL_DENIED_AUTH,(c->flags & CLIENT_MULTI) ? ACL_LOG_CTX_MULTI : ACL_LOG_CTX_TOPLEVEL,0,username->ptr,NULL);
1190 return C_ERR;
1191 }
1192 }
1193
1194 /* For ACL purposes, every user has a bitmap with the commands that such
1195 * user is allowed to execute. In order to populate the bitmap, every command
1196 * should have an assigned ID (that is used to index the bitmap). This function
1197 * creates such an ID: it uses sequential IDs, reusing the same ID for the same
1198 * command name, so that a command retains the same ID in case of modules that
1199 * are unloaded and later reloaded. */
ACLGetCommandID(const char * cmdname)1200 unsigned long ACLGetCommandID(const char *cmdname) {
1201 sds lowername = sdsnew(cmdname);
1202 sdstolower(lowername);
1203 if (commandId == NULL) commandId = raxNew();
1204 void *id = raxFind(commandId,(unsigned char*)lowername,sdslen(lowername));
1205 if (id != raxNotFound) {
1206 sdsfree(lowername);
1207 return (unsigned long)id;
1208 }
1209 raxInsert(commandId,(unsigned char*)lowername,strlen(lowername),
1210 (void*)nextid,NULL);
1211 sdsfree(lowername);
1212 unsigned long thisid = nextid;
1213 nextid++;
1214
1215 /* We never assign the last bit in the user commands bitmap structure,
1216 * this way we can later check if this bit is set, understanding if the
1217 * current ACL for the user was created starting with a +@all to add all
1218 * the possible commands and just subtracting other single commands or
1219 * categories, or if, instead, the ACL was created just adding commands
1220 * and command categories from scratch, not allowing future commands by
1221 * default (loaded via modules). This is useful when rewriting the ACLs
1222 * with ACL SAVE. */
1223 if (nextid == USER_COMMAND_BITS_COUNT-1) nextid++;
1224 return thisid;
1225 }
1226
1227 /* Clear command id table and reset nextid to 0. */
ACLClearCommandID(void)1228 void ACLClearCommandID(void) {
1229 if (commandId) raxFree(commandId);
1230 commandId = NULL;
1231 nextid = 0;
1232 }
1233
1234 /* Return an username by its name, or NULL if the user does not exist. */
ACLGetUserByName(const char * name,size_t namelen)1235 user *ACLGetUserByName(const char *name, size_t namelen) {
1236 void *myuser = raxFind(Users,(unsigned char*)name,namelen);
1237 if (myuser == raxNotFound) return NULL;
1238 return myuser;
1239 }
1240
1241 /* Check if the key can be accessed by the client according to
1242 * the ACLs associated with the specified user.
1243 *
1244 * If the user can access the key, ACL_OK is returned, otherwise
1245 * ACL_DENIED_KEY is returned. */
ACLCheckKey(const user * u,const char * key,int keylen)1246 int ACLCheckKey(const user *u, const char *key, int keylen) {
1247 /* If there is no associated user, the connection can run anything. */
1248 if (u == NULL) return ACL_OK;
1249
1250 /* The user can run any keys */
1251 if (u->flags & USER_FLAG_ALLKEYS) return ACL_OK;
1252
1253 listIter li;
1254 listNode *ln;
1255 listRewind(u->patterns,&li);
1256
1257 /* Test this key against every pattern. */
1258 while((ln = listNext(&li))) {
1259 sds pattern = listNodeValue(ln);
1260 size_t plen = sdslen(pattern);
1261 if (stringmatchlen(pattern,plen,key,keylen,0))
1262 return ACL_OK;
1263 }
1264 return ACL_DENIED_KEY;
1265 }
1266
1267 /* Check if the command is ready to be executed according to the
1268 * ACLs associated with the specified user.
1269 *
1270 * If the user can execute the command ACL_OK is returned, otherwise
1271 * ACL_DENIED_CMD or ACL_DENIED_KEY is returned: the first in case the
1272 * command cannot be executed because the user is not allowed to run such
1273 * command, the second if the command is denied because the user is trying
1274 * to access keys that are not among the specified patterns. */
ACLCheckCommandPerm(const user * u,struct redisCommand * cmd,robj ** argv,int argc,int * keyidxptr)1275 int ACLCheckCommandPerm(const user *u, struct redisCommand *cmd, robj **argv, int argc, int *keyidxptr) {
1276 int ret;
1277 uint64_t id = cmd->id;
1278
1279 /* If there is no associated user, the connection can run anything. */
1280 if (u == NULL) return ACL_OK;
1281
1282 /* Check if the user can execute this command or if the command
1283 * doesn't need to be authenticated (hello, auth). */
1284 if (!(u->flags & USER_FLAG_ALLCOMMANDS) && !(cmd->flags & CMD_NO_AUTH))
1285 {
1286 /* If the bit is not set we have to check further, in case the
1287 * command is allowed just with that specific first argument. */
1288 if (ACLGetUserCommandBit(u,id) == 0) {
1289 /* Check if the first argument matches. */
1290 if (argc < 2 ||
1291 u->allowed_firstargs == NULL ||
1292 u->allowed_firstargs[id] == NULL)
1293 {
1294 return ACL_DENIED_CMD;
1295 }
1296
1297 long subid = 0;
1298 while (1) {
1299 if (u->allowed_firstargs[id][subid] == NULL)
1300 return ACL_DENIED_CMD;
1301 int idx = cmd->parent ? 2 : 1;
1302 if (!strcasecmp(argv[idx]->ptr,u->allowed_firstargs[id][subid]))
1303 break; /* First argument match found. Stop here. */
1304 subid++;
1305 }
1306 }
1307 }
1308
1309 /* Check if the user can execute commands explicitly touching the keys
1310 * mentioned in the command arguments. */
1311 if (!(u->flags & USER_FLAG_ALLKEYS) &&
1312 (cmd->getkeys_proc || cmd->key_specs_num))
1313 {
1314 getKeysResult result = GETKEYS_RESULT_INIT;
1315 int numkeys = getKeysFromCommand(cmd,argv,argc,&result);
1316 int *keyidx = result.keys;
1317 for (int j = 0; j < numkeys; j++) {
1318 int idx = keyidx[j];
1319 ret = ACLCheckKey(u, argv[idx]->ptr, sdslen(argv[idx]->ptr));
1320 if (ret != ACL_OK) {
1321 if (keyidxptr) *keyidxptr = keyidx[j];
1322 getKeysFreeResult(&result);
1323 return ret;
1324 }
1325 }
1326 getKeysFreeResult(&result);
1327 }
1328
1329 /* If we survived all the above checks, the user can execute the
1330 * command. */
1331 return ACL_OK;
1332 }
1333
1334 /* Check if the provided channel is whitelisted by the given allowed channels
1335 * list. Glob-style pattern matching is employed, unless the literal flag is
1336 * set. Returns ACL_OK if access is granted or ACL_DENIED_CHANNEL otherwise. */
ACLCheckPubsubChannelPerm(sds channel,list * allowed,int literal)1337 int ACLCheckPubsubChannelPerm(sds channel, list *allowed, int literal) {
1338 listIter li;
1339 listNode *ln;
1340 size_t clen = sdslen(channel);
1341 int match = 0;
1342
1343 listRewind(allowed,&li);
1344 while((ln = listNext(&li))) {
1345 sds pattern = listNodeValue(ln);
1346 size_t plen = sdslen(pattern);
1347
1348 if ((literal && !sdscmp(pattern,channel)) ||
1349 (!literal && stringmatchlen(pattern,plen,channel,clen,0)))
1350 {
1351 match = 1;
1352 break;
1353 }
1354 }
1355 if (!match) {
1356 return ACL_DENIED_CHANNEL;
1357 }
1358 return ACL_OK;
1359 }
1360
1361 /* Check if the user's existing pub/sub clients violate the ACL pub/sub
1362 * permissions specified via the upcoming argument, and kill them if so. */
ACLKillPubsubClientsIfNeeded(user * u,list * upcoming)1363 void ACLKillPubsubClientsIfNeeded(user *u, list *upcoming) {
1364 listIter li, lpi;
1365 listNode *ln, *lpn;
1366 robj *o;
1367 int kill = 0;
1368
1369 /* Nothing to kill when the upcoming are a literal super set of the original
1370 * permissions. */
1371 listRewind(u->channels,&li);
1372 while (!kill && ((ln = listNext(&li)) != NULL)) {
1373 sds pattern = listNodeValue(ln);
1374 kill = (ACLCheckPubsubChannelPerm(pattern,upcoming,1) ==
1375 ACL_DENIED_CHANNEL);
1376 }
1377 if (!kill) return;
1378
1379 /* Scan all connected clients to find the user's pub/subs. */
1380 listRewind(server.clients,&li);
1381 while ((ln = listNext(&li)) != NULL) {
1382 client *c = listNodeValue(ln);
1383 kill = 0;
1384
1385 if (c->user == u && getClientType(c) == CLIENT_TYPE_PUBSUB) {
1386 /* Check for pattern violations. */
1387 listRewind(c->pubsub_patterns,&lpi);
1388 while (!kill && ((lpn = listNext(&lpi)) != NULL)) {
1389 o = lpn->value;
1390 kill = (ACLCheckPubsubChannelPerm(o->ptr,upcoming,1) ==
1391 ACL_DENIED_CHANNEL);
1392 }
1393 /* Check for channel violations. */
1394 if (!kill) {
1395 dictIterator *di = dictGetIterator(c->pubsub_channels);
1396 dictEntry *de;
1397 while (!kill && ((de = dictNext(di)) != NULL)) {
1398 o = dictGetKey(de);
1399 kill = (ACLCheckPubsubChannelPerm(o->ptr,upcoming,0) ==
1400 ACL_DENIED_CHANNEL);
1401 }
1402 dictReleaseIterator(di);
1403 }
1404
1405 /* Kill it. */
1406 if (kill) {
1407 freeClient(c);
1408 }
1409 }
1410 }
1411 }
1412
1413 /* Check if the pub/sub channels of the command, that's ready to be executed
1414 * according to the ACLs channels associated with the specified user.
1415 *
1416 * idx and count are the index and count of channel arguments from the
1417 * command. The literal argument controls whether the user's ACL channels are
1418 * evaluated as literal values or matched as glob-like patterns.
1419 *
1420 * If the user can execute the command ACL_OK is returned, otherwise
1421 * ACL_DENIED_CHANNEL. */
ACLCheckPubsubPerm(const user * u,robj ** argv,int idx,int count,int literal,int * idxptr)1422 int ACLCheckPubsubPerm(const user *u, robj **argv, int idx, int count, int literal, int *idxptr) {
1423 /* If there is no associated user, the connection can run anything. */
1424 if (u == NULL) return ACL_OK;
1425
1426 /* Check if the user can access the channels mentioned in the command's
1427 * arguments. */
1428 if (!(u->flags & USER_FLAG_ALLCHANNELS)) {
1429 for (int j = idx; j < idx+count; j++) {
1430 if (ACLCheckPubsubChannelPerm(argv[j]->ptr,u->channels,literal)
1431 != ACL_OK) {
1432 if (idxptr) *idxptr = j;
1433 return ACL_DENIED_CHANNEL;
1434 }
1435 }
1436 }
1437
1438 /* If we survived all the above checks, the user can execute the
1439 * command. */
1440 return ACL_OK;
1441
1442 }
1443
1444 /* Check whether the command is ready to be executed by ACLCheckCommandPerm.
1445 * If check passes, then check whether pub/sub channels of the command is
1446 * ready to be executed by ACLCheckPubsubPerm */
ACLCheckAllUserCommandPerm(const user * u,struct redisCommand * cmd,robj ** argv,int argc,int * idxptr)1447 int ACLCheckAllUserCommandPerm(const user *u, struct redisCommand *cmd, robj **argv, int argc, int *idxptr) {
1448 int acl_retval = ACLCheckCommandPerm(u,cmd,argv,argc,idxptr);
1449 if (acl_retval != ACL_OK)
1450 return acl_retval;
1451 if (cmd->proc == publishCommand)
1452 acl_retval = ACLCheckPubsubPerm(u,argv,1,1,0,idxptr);
1453 else if (cmd->proc == subscribeCommand)
1454 acl_retval = ACLCheckPubsubPerm(u,argv,1,argc-1,0,idxptr);
1455 else if (cmd->proc == psubscribeCommand)
1456 acl_retval = ACLCheckPubsubPerm(u,argv,1,argc-1,1,idxptr);
1457 return acl_retval;
1458 }
1459
ACLCheckAllPerm(client * c,int * idxptr)1460 int ACLCheckAllPerm(client *c, int *idxptr) {
1461 return ACLCheckAllUserCommandPerm(c->user, c->cmd, c->argv, c->argc, idxptr);
1462 }
1463
1464 /* =============================================================================
1465 * ACL loading / saving functions
1466 * ==========================================================================*/
1467
1468 /* Given an argument vector describing a user in the form:
1469 *
1470 * user <username> ... ACL rules and flags ...
1471 *
1472 * this function validates, and if the syntax is valid, appends
1473 * the user definition to a list for later loading.
1474 *
1475 * The rules are tested for validity and if there obvious syntax errors
1476 * the function returns C_ERR and does nothing, otherwise C_OK is returned
1477 * and the user is appended to the list.
1478 *
1479 * Note that this function cannot stop in case of commands that are not found
1480 * and, in that case, the error will be emitted later, because certain
1481 * commands may be defined later once modules are loaded.
1482 *
1483 * When an error is detected and C_ERR is returned, the function populates
1484 * by reference (if not set to NULL) the argc_err argument with the index
1485 * of the argv vector that caused the error. */
ACLAppendUserForLoading(sds * argv,int argc,int * argc_err)1486 int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {
1487 if (argc < 2 || strcasecmp(argv[0],"user")) {
1488 if (argc_err) *argc_err = 0;
1489 return C_ERR;
1490 }
1491
1492 if (listSearchKey(UsersToLoad, argv[1])) {
1493 if (argc_err) *argc_err = 1;
1494 errno = EALREADY;
1495 return C_ERR;
1496 }
1497
1498 /* Try to apply the user rules in a fake user to see if they
1499 * are actually valid. */
1500 user *fakeuser = ACLCreateUnlinkedUser();
1501
1502 for (int j = 2; j < argc; j++) {
1503 if (ACLSetUser(fakeuser,argv[j],sdslen(argv[j])) == C_ERR) {
1504 if (errno != ENOENT) {
1505 ACLFreeUser(fakeuser);
1506 if (argc_err) *argc_err = j;
1507 return C_ERR;
1508 }
1509 }
1510 }
1511
1512 /* Rules look valid, let's append the user to the list. */
1513 sds *copy = zmalloc(sizeof(sds)*argc);
1514 for (int j = 1; j < argc; j++) copy[j-1] = sdsdup(argv[j]);
1515 copy[argc-1] = NULL;
1516 listAddNodeTail(UsersToLoad,copy);
1517 ACLFreeUser(fakeuser);
1518 return C_OK;
1519 }
1520
1521 /* This function will load the configured users appended to the server
1522 * configuration via ACLAppendUserForLoading(). On loading errors it will
1523 * log an error and return C_ERR, otherwise C_OK will be returned. */
ACLLoadConfiguredUsers(void)1524 int ACLLoadConfiguredUsers(void) {
1525 listIter li;
1526 listNode *ln;
1527 listRewind(UsersToLoad,&li);
1528 while ((ln = listNext(&li)) != NULL) {
1529 sds *aclrules = listNodeValue(ln);
1530 sds username = aclrules[0];
1531
1532 if (ACLStringHasSpaces(aclrules[0],sdslen(aclrules[0]))) {
1533 serverLog(LL_WARNING,"Spaces not allowed in ACL usernames");
1534 return C_ERR;
1535 }
1536
1537 user *u = ACLCreateUser(username,sdslen(username));
1538 if (!u) {
1539 /* Only valid duplicate user is the default one. */
1540 serverAssert(!strcmp(username, "default"));
1541 u = ACLGetUserByName("default",7);
1542 ACLSetUser(u,"reset",-1);
1543 }
1544
1545 /* Load every rule defined for this user. */
1546 for (int j = 1; aclrules[j]; j++) {
1547 if (ACLSetUser(u,aclrules[j],sdslen(aclrules[j])) != C_OK) {
1548 const char *errmsg = ACLSetUserStringError();
1549 serverLog(LL_WARNING,"Error loading ACL rule '%s' for "
1550 "the user named '%s': %s",
1551 aclrules[j],aclrules[0],errmsg);
1552 return C_ERR;
1553 }
1554 }
1555
1556 /* Having a disabled user in the configuration may be an error,
1557 * warn about it without returning any error to the caller. */
1558 if (u->flags & USER_FLAG_DISABLED) {
1559 serverLog(LL_NOTICE, "The user '%s' is disabled (there is no "
1560 "'on' modifier in the user description). Make "
1561 "sure this is not a configuration error.",
1562 aclrules[0]);
1563 }
1564 }
1565 return C_OK;
1566 }
1567
1568 /* This function loads the ACL from the specified filename: every line
1569 * is validated and should be either empty or in the format used to specify
1570 * users in the redis.conf configuration or in the ACL file, that is:
1571 *
1572 * user <username> ... rules ...
1573 *
1574 * Note that this function considers comments starting with '#' as errors
1575 * because the ACL file is meant to be rewritten, and comments would be
1576 * lost after the rewrite. Yet empty lines are allowed to avoid being too
1577 * strict.
1578 *
1579 * One important part of implementing ACL LOAD, that uses this function, is
1580 * to avoid ending with broken rules if the ACL file is invalid for some
1581 * reason, so the function will attempt to validate the rules before loading
1582 * each user. For every line that will be found broken the function will
1583 * collect an error message.
1584 *
1585 * IMPORTANT: If there is at least a single error, nothing will be loaded
1586 * and the rules will remain exactly as they were.
1587 *
1588 * At the end of the process, if no errors were found in the whole file then
1589 * NULL is returned. Otherwise an SDS string describing in a single line
1590 * a description of all the issues found is returned. */
ACLLoadFromFile(const char * filename)1591 sds ACLLoadFromFile(const char *filename) {
1592 FILE *fp;
1593 char buf[1024];
1594
1595 /* Open the ACL file. */
1596 if ((fp = fopen(filename,"r")) == NULL) {
1597 sds errors = sdscatprintf(sdsempty(),
1598 "Error loading ACLs, opening file '%s': %s",
1599 filename, strerror(errno));
1600 return errors;
1601 }
1602
1603 /* Load the whole file as a single string in memory. */
1604 sds acls = sdsempty();
1605 while(fgets(buf,sizeof(buf),fp) != NULL)
1606 acls = sdscat(acls,buf);
1607 fclose(fp);
1608
1609 /* Split the file into lines and attempt to load each line. */
1610 int totlines;
1611 sds *lines, errors = sdsempty();
1612 lines = sdssplitlen(acls,strlen(acls),"\n",1,&totlines);
1613 sdsfree(acls);
1614
1615 /* We do all the loading in a fresh instance of the Users radix tree,
1616 * so if there are errors loading the ACL file we can rollback to the
1617 * old version. */
1618 rax *old_users = Users;
1619 Users = raxNew();
1620
1621 /* Load each line of the file. */
1622 for (int i = 0; i < totlines; i++) {
1623 sds *argv;
1624 int argc;
1625 int linenum = i+1;
1626
1627 lines[i] = sdstrim(lines[i]," \t\r\n");
1628
1629 /* Skip blank lines */
1630 if (lines[i][0] == '\0') continue;
1631
1632 /* Split into arguments */
1633 argv = sdssplitlen(lines[i],sdslen(lines[i])," ",1,&argc);
1634 if (argv == NULL) {
1635 errors = sdscatprintf(errors,
1636 "%s:%d: unbalanced quotes in acl line. ",
1637 server.acl_filename, linenum);
1638 continue;
1639 }
1640
1641 /* Skip this line if the resulting command vector is empty. */
1642 if (argc == 0) {
1643 sdsfreesplitres(argv,argc);
1644 continue;
1645 }
1646
1647 /* The line should start with the "user" keyword. */
1648 if (strcmp(argv[0],"user") || argc < 2) {
1649 errors = sdscatprintf(errors,
1650 "%s:%d should start with user keyword followed "
1651 "by the username. ", server.acl_filename,
1652 linenum);
1653 sdsfreesplitres(argv,argc);
1654 continue;
1655 }
1656
1657 /* Spaces are not allowed in usernames. */
1658 if (ACLStringHasSpaces(argv[1],sdslen(argv[1]))) {
1659 errors = sdscatprintf(errors,
1660 "'%s:%d: username '%s' contains invalid characters. ",
1661 server.acl_filename, linenum, argv[1]);
1662 sdsfreesplitres(argv,argc);
1663 continue;
1664 }
1665
1666 user *u = ACLCreateUser(argv[1],sdslen(argv[1]));
1667
1668 /* If the user already exists we assume it's an error and abort. */
1669 if (!u) {
1670 errors = sdscatprintf(errors,"WARNING: Duplicate user '%s' found on line %d. ", argv[1], linenum);
1671 sdsfreesplitres(argv,argc);
1672 continue;
1673 }
1674
1675 /* Finally process the options and validate they can
1676 * be cleanly applied to the user. If any option fails
1677 * to apply, the other values won't be applied since
1678 * all the pending changes will get dropped. */
1679 int j;
1680 for (j = 2; j < argc; j++) {
1681 argv[j] = sdstrim(argv[j],"\t\r\n");
1682 if (ACLSetUser(u,argv[j],sdslen(argv[j])) != C_OK) {
1683 const char *errmsg = ACLSetUserStringError();
1684 errors = sdscatprintf(errors,
1685 "%s:%d: %s. ",
1686 server.acl_filename, linenum, errmsg);
1687 continue;
1688 }
1689 }
1690
1691 /* Apply the rule to the new users set only if so far there
1692 * are no errors, otherwise it's useless since we are going
1693 * to discard the new users set anyway. */
1694 if (sdslen(errors) != 0) {
1695 sdsfreesplitres(argv,argc);
1696 continue;
1697 }
1698
1699 sdsfreesplitres(argv,argc);
1700 }
1701
1702 sdsfreesplitres(lines,totlines);
1703
1704 /* Check if we found errors and react accordingly. */
1705 if (sdslen(errors) == 0) {
1706 /* The default user pointer is referenced in different places: instead
1707 * of replacing such occurrences it is much simpler to copy the new
1708 * default user configuration in the old one. */
1709 user *new_default = ACLGetUserByName("default",7);
1710 if (!new_default) {
1711 new_default = ACLCreateDefaultUser();
1712 }
1713
1714 ACLCopyUser(DefaultUser,new_default);
1715 ACLFreeUser(new_default);
1716 raxInsert(Users,(unsigned char*)"default",7,DefaultUser,NULL);
1717 raxRemove(old_users,(unsigned char*)"default",7,NULL);
1718 ACLFreeUsersSet(old_users);
1719 sdsfree(errors);
1720 return NULL;
1721 } else {
1722 ACLFreeUsersSet(Users);
1723 Users = old_users;
1724 errors = sdscat(errors,"WARNING: ACL errors detected, no change to the previously active ACL rules was performed");
1725 return errors;
1726 }
1727 }
1728
1729 /* Generate a copy of the ACLs currently in memory in the specified filename.
1730 * Returns C_OK on success or C_ERR if there was an error during the I/O.
1731 * When C_ERR is returned a log is produced with hints about the issue. */
ACLSaveToFile(const char * filename)1732 int ACLSaveToFile(const char *filename) {
1733 sds acl = sdsempty();
1734 int fd = -1;
1735 sds tmpfilename = NULL;
1736 int retval = C_ERR;
1737
1738 /* Let's generate an SDS string containing the new version of the
1739 * ACL file. */
1740 raxIterator ri;
1741 raxStart(&ri,Users);
1742 raxSeek(&ri,"^",NULL,0);
1743 while(raxNext(&ri)) {
1744 user *u = ri.data;
1745 /* Return information in the configuration file format. */
1746 sds user = sdsnew("user ");
1747 user = sdscatsds(user,u->name);
1748 user = sdscatlen(user," ",1);
1749 sds descr = ACLDescribeUser(u);
1750 user = sdscatsds(user,descr);
1751 sdsfree(descr);
1752 acl = sdscatsds(acl,user);
1753 acl = sdscatlen(acl,"\n",1);
1754 sdsfree(user);
1755 }
1756 raxStop(&ri);
1757
1758 /* Create a temp file with the new content. */
1759 tmpfilename = sdsnew(filename);
1760 tmpfilename = sdscatfmt(tmpfilename,".tmp-%i-%I",
1761 (int)getpid(),(int)mstime());
1762 if ((fd = open(tmpfilename,O_WRONLY|O_CREAT,0644)) == -1) {
1763 serverLog(LL_WARNING,"Opening temp ACL file for ACL SAVE: %s",
1764 strerror(errno));
1765 goto cleanup;
1766 }
1767
1768 /* Write it. */
1769 if (write(fd,acl,sdslen(acl)) != (ssize_t)sdslen(acl)) {
1770 serverLog(LL_WARNING,"Writing ACL file for ACL SAVE: %s",
1771 strerror(errno));
1772 goto cleanup;
1773 }
1774 close(fd); fd = -1;
1775
1776 /* Let's replace the new file with the old one. */
1777 if (rename(tmpfilename,filename) == -1) {
1778 serverLog(LL_WARNING,"Renaming ACL file for ACL SAVE: %s",
1779 strerror(errno));
1780 goto cleanup;
1781 }
1782 sdsfree(tmpfilename); tmpfilename = NULL;
1783 retval = C_OK; /* If we reached this point, everything is fine. */
1784
1785 cleanup:
1786 if (fd != -1) close(fd);
1787 if (tmpfilename) unlink(tmpfilename);
1788 sdsfree(tmpfilename);
1789 sdsfree(acl);
1790 return retval;
1791 }
1792
1793 /* This function is called once the server is already running, modules are
1794 * loaded, and we are ready to start, in order to load the ACLs either from
1795 * the pending list of users defined in redis.conf, or from the ACL file.
1796 * The function will just exit with an error if the user is trying to mix
1797 * both the loading methods. */
ACLLoadUsersAtStartup(void)1798 void ACLLoadUsersAtStartup(void) {
1799 if (server.acl_filename[0] != '\0' && listLength(UsersToLoad) != 0) {
1800 serverLog(LL_WARNING,
1801 "Configuring Redis with users defined in redis.conf and at "
1802 "the same setting an ACL file path is invalid. This setup "
1803 "is very likely to lead to configuration errors and security "
1804 "holes, please define either an ACL file or declare users "
1805 "directly in your redis.conf, but not both.");
1806 exit(1);
1807 }
1808
1809 if (ACLLoadConfiguredUsers() == C_ERR) {
1810 serverLog(LL_WARNING,
1811 "Critical error while loading ACLs. Exiting.");
1812 exit(1);
1813 }
1814
1815 if (server.acl_filename[0] != '\0') {
1816 sds errors = ACLLoadFromFile(server.acl_filename);
1817 if (errors) {
1818 serverLog(LL_WARNING,
1819 "Aborting Redis startup because of ACL errors: %s", errors);
1820 sdsfree(errors);
1821 exit(1);
1822 }
1823 }
1824 }
1825
1826 /* =============================================================================
1827 * ACL log
1828 * ==========================================================================*/
1829
1830 #define ACL_LOG_GROUPING_MAX_TIME_DELTA 60000
1831
1832 /* This structure defines an entry inside the ACL log. */
1833 typedef struct ACLLogEntry {
1834 uint64_t count; /* Number of times this happened recently. */
1835 int reason; /* Reason for denying the command. ACL_DENIED_*. */
1836 int context; /* Toplevel, Lua or MULTI/EXEC? ACL_LOG_CTX_*. */
1837 sds object; /* The key name or command name. */
1838 sds username; /* User the client is authenticated with. */
1839 mstime_t ctime; /* Milliseconds time of last update to this entry. */
1840 sds cinfo; /* Client info (last client if updated). */
1841 } ACLLogEntry;
1842
1843 /* This function will check if ACL entries 'a' and 'b' are similar enough
1844 * that we should actually update the existing entry in our ACL log instead
1845 * of creating a new one. */
ACLLogMatchEntry(ACLLogEntry * a,ACLLogEntry * b)1846 int ACLLogMatchEntry(ACLLogEntry *a, ACLLogEntry *b) {
1847 if (a->reason != b->reason) return 0;
1848 if (a->context != b->context) return 0;
1849 mstime_t delta = a->ctime - b->ctime;
1850 if (delta < 0) delta = -delta;
1851 if (delta > ACL_LOG_GROUPING_MAX_TIME_DELTA) return 0;
1852 if (sdscmp(a->object,b->object) != 0) return 0;
1853 if (sdscmp(a->username,b->username) != 0) return 0;
1854 return 1;
1855 }
1856
1857 /* Release an ACL log entry. */
ACLFreeLogEntry(void * leptr)1858 void ACLFreeLogEntry(void *leptr) {
1859 ACLLogEntry *le = leptr;
1860 sdsfree(le->object);
1861 sdsfree(le->username);
1862 sdsfree(le->cinfo);
1863 zfree(le);
1864 }
1865
1866 /* Adds a new entry in the ACL log, making sure to delete the old entry
1867 * if we reach the maximum length allowed for the log. This function attempts
1868 * to find similar entries in the current log in order to bump the counter of
1869 * the log entry instead of creating many entries for very similar ACL
1870 * rules issues.
1871 *
1872 * The argpos argument is used when the reason is ACL_DENIED_KEY or
1873 * ACL_DENIED_CHANNEL, since it allows the function to log the key or channel
1874 * name that caused the problem.
1875 *
1876 * The last 2 arguments are a manual override to be used, instead of any of the automatic
1877 * ones which depend on the client and reason arguments (use NULL for default).
1878 */
addACLLogEntry(client * c,int reason,int context,int argpos,sds username,sds object)1879 void addACLLogEntry(client *c, int reason, int context, int argpos, sds username, sds object) {
1880 /* Create a new entry. */
1881 struct ACLLogEntry *le = zmalloc(sizeof(*le));
1882 le->count = 1;
1883 le->reason = reason;
1884 le->username = sdsdup(username ? username : c->user->name);
1885 le->ctime = mstime();
1886
1887 if (object) {
1888 le->object = sdsnew(object);
1889 } else {
1890 switch(reason) {
1891 case ACL_DENIED_CMD: le->object = sdsnew(c->cmd->name); break;
1892 case ACL_DENIED_KEY: le->object = sdsdup(c->argv[argpos]->ptr); break;
1893 case ACL_DENIED_CHANNEL: le->object = sdsdup(c->argv[argpos]->ptr); break;
1894 case ACL_DENIED_AUTH: le->object = sdsdup(c->argv[0]->ptr); break;
1895 default: le->object = sdsempty();
1896 }
1897 }
1898
1899 client *realclient = c;
1900 if (realclient->flags & CLIENT_LUA) realclient = server.lua_caller;
1901
1902 le->cinfo = catClientInfoString(sdsempty(),realclient);
1903 le->context = context;
1904
1905 /* Try to match this entry with past ones, to see if we can just
1906 * update an existing entry instead of creating a new one. */
1907 long toscan = 10; /* Do a limited work trying to find duplicated. */
1908 listIter li;
1909 listNode *ln;
1910 listRewind(ACLLog,&li);
1911 ACLLogEntry *match = NULL;
1912 while (toscan-- && (ln = listNext(&li)) != NULL) {
1913 ACLLogEntry *current = listNodeValue(ln);
1914 if (ACLLogMatchEntry(current,le)) {
1915 match = current;
1916 listDelNode(ACLLog,ln);
1917 listAddNodeHead(ACLLog,current);
1918 break;
1919 }
1920 }
1921
1922 /* If there is a match update the entry, otherwise add it as a
1923 * new one. */
1924 if (match) {
1925 /* We update a few fields of the existing entry and bump the
1926 * counter of events for this entry. */
1927 sdsfree(match->cinfo);
1928 match->cinfo = le->cinfo;
1929 match->ctime = le->ctime;
1930 match->count++;
1931
1932 /* Release the old entry. */
1933 le->cinfo = NULL;
1934 ACLFreeLogEntry(le);
1935 } else {
1936 /* Add it to our list of entries. We'll have to trim the list
1937 * to its maximum size. */
1938 listAddNodeHead(ACLLog, le);
1939 while(listLength(ACLLog) > server.acllog_max_len) {
1940 listNode *ln = listLast(ACLLog);
1941 ACLLogEntry *le = listNodeValue(ln);
1942 ACLFreeLogEntry(le);
1943 listDelNode(ACLLog,ln);
1944 }
1945 }
1946 }
1947
1948 /* =============================================================================
1949 * ACL related commands
1950 * ==========================================================================*/
1951
1952 /* ACL -- show and modify the configuration of ACL users.
1953 * ACL HELP
1954 * ACL LOAD
1955 * ACL SAVE
1956 * ACL LIST
1957 * ACL USERS
1958 * ACL CAT [<category>]
1959 * ACL SETUSER <username> ... acl rules ...
1960 * ACL DELUSER <username> [...]
1961 * ACL GETUSER <username>
1962 * ACL GENPASS [<bits>]
1963 * ACL WHOAMI
1964 * ACL LOG [<count> | RESET]
1965 */
aclCommand(client * c)1966 void aclCommand(client *c) {
1967 char *sub = c->argv[1]->ptr;
1968 if (!strcasecmp(sub,"setuser") && c->argc >= 3) {
1969 sds username = c->argv[2]->ptr;
1970 /* Check username validity. */
1971 if (ACLStringHasSpaces(username,sdslen(username))) {
1972 addReplyErrorFormat(c,
1973 "Usernames can't contain spaces or null characters");
1974 return;
1975 }
1976
1977 /* Create a temporary user to validate and stage all changes against
1978 * before applying to an existing user or creating a new user. If all
1979 * arguments are valid the user parameters will all be applied together.
1980 * If there are any errors then none of the changes will be applied. */
1981 user *tempu = ACLCreateUnlinkedUser();
1982 user *u = ACLGetUserByName(username,sdslen(username));
1983 if (u) ACLCopyUser(tempu, u);
1984
1985 /* Initially redact all of the arguments to not leak any information
1986 * about the user. */
1987 for (int j = 2; j < c->argc; j++) {
1988 redactClientCommandArgument(c, j);
1989 }
1990
1991 for (int j = 3; j < c->argc; j++) {
1992 if (ACLSetUser(tempu,c->argv[j]->ptr,sdslen(c->argv[j]->ptr)) != C_OK) {
1993 const char *errmsg = ACLSetUserStringError();
1994 addReplyErrorFormat(c,
1995 "Error in ACL SETUSER modifier '%s': %s",
1996 (char*)c->argv[j]->ptr, errmsg);
1997
1998 ACLFreeUser(tempu);
1999 return;
2000 }
2001 }
2002
2003 /* Existing pub/sub clients authenticated with the user may need to be
2004 * disconnected if (some of) their channel permissions were revoked. */
2005 if (u && !(tempu->flags & USER_FLAG_ALLCHANNELS))
2006 ACLKillPubsubClientsIfNeeded(u,tempu->channels);
2007
2008 /* Overwrite the user with the temporary user we modified above. */
2009 if (!u) u = ACLCreateUser(username,sdslen(username));
2010 serverAssert(u != NULL);
2011 ACLCopyUser(u, tempu);
2012 ACLFreeUser(tempu);
2013 addReply(c,shared.ok);
2014 } else if (!strcasecmp(sub,"deluser") && c->argc >= 3) {
2015 int deleted = 0;
2016 for (int j = 2; j < c->argc; j++) {
2017 sds username = c->argv[j]->ptr;
2018 if (!strcmp(username,"default")) {
2019 addReplyError(c,"The 'default' user cannot be removed");
2020 return;
2021 }
2022 }
2023
2024 for (int j = 2; j < c->argc; j++) {
2025 sds username = c->argv[j]->ptr;
2026 user *u;
2027 if (raxRemove(Users,(unsigned char*)username,
2028 sdslen(username),
2029 (void**)&u))
2030 {
2031 ACLFreeUserAndKillClients(u);
2032 deleted++;
2033 }
2034 }
2035 addReplyLongLong(c,deleted);
2036 } else if (!strcasecmp(sub,"getuser") && c->argc == 3) {
2037 user *u = ACLGetUserByName(c->argv[2]->ptr,sdslen(c->argv[2]->ptr));
2038 if (u == NULL) {
2039 addReplyNull(c);
2040 return;
2041 }
2042
2043 addReplyMapLen(c,5);
2044
2045 /* Flags */
2046 addReplyBulkCString(c,"flags");
2047 void *deflen = addReplyDeferredLen(c);
2048 int numflags = 0;
2049 for (int j = 0; ACLUserFlags[j].flag; j++) {
2050 if (u->flags & ACLUserFlags[j].flag) {
2051 addReplyBulkCString(c,ACLUserFlags[j].name);
2052 numflags++;
2053 }
2054 }
2055 setDeferredSetLen(c,deflen,numflags);
2056
2057 /* Passwords */
2058 addReplyBulkCString(c,"passwords");
2059 addReplyArrayLen(c,listLength(u->passwords));
2060 listIter li;
2061 listNode *ln;
2062 listRewind(u->passwords,&li);
2063 while((ln = listNext(&li))) {
2064 sds thispass = listNodeValue(ln);
2065 addReplyBulkCBuffer(c,thispass,sdslen(thispass));
2066 }
2067
2068 /* Commands */
2069 addReplyBulkCString(c,"commands");
2070 sds cmddescr = ACLDescribeUserCommandRules(u);
2071 addReplyBulkSds(c,cmddescr);
2072
2073 /* Key patterns */
2074 addReplyBulkCString(c,"keys");
2075 if (u->flags & USER_FLAG_ALLKEYS) {
2076 addReplyArrayLen(c,1);
2077 addReplyBulkCBuffer(c,"*",1);
2078 } else {
2079 addReplyArrayLen(c,listLength(u->patterns));
2080 listIter li;
2081 listNode *ln;
2082 listRewind(u->patterns,&li);
2083 while((ln = listNext(&li))) {
2084 sds thispat = listNodeValue(ln);
2085 addReplyBulkCBuffer(c,thispat,sdslen(thispat));
2086 }
2087 }
2088
2089 /* Pub/sub patterns */
2090 addReplyBulkCString(c,"channels");
2091 if (u->flags & USER_FLAG_ALLCHANNELS) {
2092 addReplyArrayLen(c,1);
2093 addReplyBulkCBuffer(c,"*",1);
2094 } else {
2095 addReplyArrayLen(c,listLength(u->channels));
2096 listIter li;
2097 listNode *ln;
2098 listRewind(u->channels,&li);
2099 while((ln = listNext(&li))) {
2100 sds thispat = listNodeValue(ln);
2101 addReplyBulkCBuffer(c,thispat,sdslen(thispat));
2102 }
2103 }
2104 } else if ((!strcasecmp(sub,"list") || !strcasecmp(sub,"users")) &&
2105 c->argc == 2)
2106 {
2107 int justnames = !strcasecmp(sub,"users");
2108 addReplyArrayLen(c,raxSize(Users));
2109 raxIterator ri;
2110 raxStart(&ri,Users);
2111 raxSeek(&ri,"^",NULL,0);
2112 while(raxNext(&ri)) {
2113 user *u = ri.data;
2114 if (justnames) {
2115 addReplyBulkCBuffer(c,u->name,sdslen(u->name));
2116 } else {
2117 /* Return information in the configuration file format. */
2118 sds config = sdsnew("user ");
2119 config = sdscatsds(config,u->name);
2120 config = sdscatlen(config," ",1);
2121 sds descr = ACLDescribeUser(u);
2122 config = sdscatsds(config,descr);
2123 sdsfree(descr);
2124 addReplyBulkSds(c,config);
2125 }
2126 }
2127 raxStop(&ri);
2128 } else if (!strcasecmp(sub,"whoami") && c->argc == 2) {
2129 if (c->user != NULL) {
2130 addReplyBulkCBuffer(c,c->user->name,sdslen(c->user->name));
2131 } else {
2132 addReplyNull(c);
2133 }
2134 } else if (server.acl_filename[0] == '\0' &&
2135 (!strcasecmp(sub,"load") || !strcasecmp(sub,"save")))
2136 {
2137 addReplyError(c,"This Redis instance is not configured to use an ACL file. You may want to specify users via the ACL SETUSER command and then issue a CONFIG REWRITE (assuming you have a Redis configuration file set) in order to store users in the Redis configuration.");
2138 return;
2139 } else if (!strcasecmp(sub,"load") && c->argc == 2) {
2140 sds errors = ACLLoadFromFile(server.acl_filename);
2141 if (errors == NULL) {
2142 addReply(c,shared.ok);
2143 } else {
2144 addReplyError(c,errors);
2145 sdsfree(errors);
2146 }
2147 } else if (!strcasecmp(sub,"save") && c->argc == 2) {
2148 if (ACLSaveToFile(server.acl_filename) == C_OK) {
2149 addReply(c,shared.ok);
2150 } else {
2151 addReplyError(c,"There was an error trying to save the ACLs. "
2152 "Please check the server logs for more "
2153 "information");
2154 }
2155 } else if (!strcasecmp(sub,"cat") && c->argc == 2) {
2156 void *dl = addReplyDeferredLen(c);
2157 int j;
2158 for (j = 0; ACLCommandCategories[j].flag != 0; j++)
2159 addReplyBulkCString(c,ACLCommandCategories[j].name);
2160 setDeferredArrayLen(c,dl,j);
2161 } else if (!strcasecmp(sub,"cat") && c->argc == 3) {
2162 uint64_t cflag = ACLGetCommandCategoryFlagByName(c->argv[2]->ptr);
2163 if (cflag == 0) {
2164 addReplyErrorFormat(c, "Unknown category '%s'", (char*)c->argv[2]->ptr);
2165 return;
2166 }
2167 int arraylen = 0;
2168 void *dl = addReplyDeferredLen(c);
2169 dictIterator *di = dictGetIterator(server.orig_commands);
2170 dictEntry *de;
2171 while ((de = dictNext(di)) != NULL) {
2172 struct redisCommand *cmd = dictGetVal(de);
2173 if (cmd->flags & CMD_MODULE) continue;
2174 if (cmd->flags & cflag) {
2175 addReplyBulkCString(c,cmd->name);
2176 arraylen++;
2177 }
2178 }
2179 dictReleaseIterator(di);
2180 setDeferredArrayLen(c,dl,arraylen);
2181 } else if (!strcasecmp(sub,"genpass") && (c->argc == 2 || c->argc == 3)) {
2182 #define GENPASS_MAX_BITS 4096
2183 char pass[GENPASS_MAX_BITS/8*2]; /* Hex representation. */
2184 long bits = 256; /* By default generate 256 bits passwords. */
2185
2186 if (c->argc == 3 && getLongFromObjectOrReply(c,c->argv[2],&bits,NULL)
2187 != C_OK) return;
2188
2189 if (bits <= 0 || bits > GENPASS_MAX_BITS) {
2190 addReplyErrorFormat(c,
2191 "ACL GENPASS argument must be the number of "
2192 "bits for the output password, a positive number "
2193 "up to %d",GENPASS_MAX_BITS);
2194 return;
2195 }
2196
2197 long chars = (bits+3)/4; /* Round to number of characters to emit. */
2198 getRandomHexChars(pass,chars);
2199 addReplyBulkCBuffer(c,pass,chars);
2200 } else if (!strcasecmp(sub,"log") && (c->argc == 2 || c->argc ==3)) {
2201 long count = 10; /* Number of entries to emit by default. */
2202
2203 /* Parse the only argument that LOG may have: it could be either
2204 * the number of entries the user wants to display, or alternatively
2205 * the "RESET" command in order to flush the old entries. */
2206 if (c->argc == 3) {
2207 if (!strcasecmp(c->argv[2]->ptr,"reset")) {
2208 listSetFreeMethod(ACLLog,ACLFreeLogEntry);
2209 listEmpty(ACLLog);
2210 listSetFreeMethod(ACLLog,NULL);
2211 addReply(c,shared.ok);
2212 return;
2213 } else if (getLongFromObjectOrReply(c,c->argv[2],&count,NULL)
2214 != C_OK)
2215 {
2216 return;
2217 }
2218 if (count < 0) count = 0;
2219 }
2220
2221 /* Fix the count according to the number of entries we got. */
2222 if ((size_t)count > listLength(ACLLog))
2223 count = listLength(ACLLog);
2224
2225 addReplyArrayLen(c,count);
2226 listIter li;
2227 listNode *ln;
2228 listRewind(ACLLog,&li);
2229 mstime_t now = mstime();
2230 while (count-- && (ln = listNext(&li)) != NULL) {
2231 ACLLogEntry *le = listNodeValue(ln);
2232 addReplyMapLen(c,7);
2233 addReplyBulkCString(c,"count");
2234 addReplyLongLong(c,le->count);
2235
2236 addReplyBulkCString(c,"reason");
2237 char *reasonstr;
2238 switch(le->reason) {
2239 case ACL_DENIED_CMD: reasonstr="command"; break;
2240 case ACL_DENIED_KEY: reasonstr="key"; break;
2241 case ACL_DENIED_CHANNEL: reasonstr="channel"; break;
2242 case ACL_DENIED_AUTH: reasonstr="auth"; break;
2243 default: reasonstr="unknown";
2244 }
2245 addReplyBulkCString(c,reasonstr);
2246
2247 addReplyBulkCString(c,"context");
2248 char *ctxstr;
2249 switch(le->context) {
2250 case ACL_LOG_CTX_TOPLEVEL: ctxstr="toplevel"; break;
2251 case ACL_LOG_CTX_MULTI: ctxstr="multi"; break;
2252 case ACL_LOG_CTX_LUA: ctxstr="lua"; break;
2253 case ACL_LOG_CTX_MODULE: ctxstr="module"; break;
2254 default: ctxstr="unknown";
2255 }
2256 addReplyBulkCString(c,ctxstr);
2257
2258 addReplyBulkCString(c,"object");
2259 addReplyBulkCBuffer(c,le->object,sdslen(le->object));
2260 addReplyBulkCString(c,"username");
2261 addReplyBulkCBuffer(c,le->username,sdslen(le->username));
2262 addReplyBulkCString(c,"age-seconds");
2263 double age = (double)(now - le->ctime)/1000;
2264 addReplyDouble(c,age);
2265 addReplyBulkCString(c,"client-info");
2266 addReplyBulkCBuffer(c,le->cinfo,sdslen(le->cinfo));
2267 }
2268 } else if (c->argc == 2 && !strcasecmp(sub,"help")) {
2269 const char *help[] = {
2270 "CAT [<category>]",
2271 " List all commands that belong to <category>, or all command categories",
2272 " when no category is specified.",
2273 "DELUSER <username> [<username> ...]",
2274 " Delete a list of users.",
2275 "GETUSER <username>",
2276 " Get the user's details.",
2277 "GENPASS [<bits>]",
2278 " Generate a secure 256-bit user password. The optional `bits` argument can",
2279 " be used to specify a different size.",
2280 "LIST",
2281 " Show users details in config file format.",
2282 "LOAD",
2283 " Reload users from the ACL file.",
2284 "LOG [<count> | RESET]",
2285 " Show the ACL log entries.",
2286 "SAVE",
2287 " Save the current config to the ACL file.",
2288 "SETUSER <username> <attribute> [<attribute> ...]",
2289 " Create or modify a user with the specified attributes.",
2290 "USERS",
2291 " List all the registered usernames.",
2292 "WHOAMI",
2293 " Return the current connection username.",
2294 NULL
2295 };
2296 addReplyHelp(c,help);
2297 } else {
2298 addReplySubcommandSyntaxError(c);
2299 }
2300 }
2301
addReplyCommandCategories(client * c,struct redisCommand * cmd)2302 void addReplyCommandCategories(client *c, struct redisCommand *cmd) {
2303 int flagcount = 0;
2304 void *flaglen = addReplyDeferredLen(c);
2305 for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {
2306 if (cmd->flags & ACLCommandCategories[j].flag) {
2307 addReplyStatusFormat(c, "@%s", ACLCommandCategories[j].name);
2308 flagcount++;
2309 }
2310 }
2311 setDeferredSetLen(c, flaglen, flagcount);
2312 }
2313
2314 /* AUTH <password>
2315 * AUTH <username> <password> (Redis >= 6.0 form)
2316 *
2317 * When the user is omitted it means that we are trying to authenticate
2318 * against the default user. */
authCommand(client * c)2319 void authCommand(client *c) {
2320 /* Only two or three argument forms are allowed. */
2321 if (c->argc > 3) {
2322 addReplyErrorObject(c,shared.syntaxerr);
2323 return;
2324 }
2325 /* Always redact the second argument */
2326 redactClientCommandArgument(c, 1);
2327
2328 /* Handle the two different forms here. The form with two arguments
2329 * will just use "default" as username. */
2330 robj *username, *password;
2331 if (c->argc == 2) {
2332 /* Mimic the old behavior of giving an error for the two argument
2333 * form if no password is configured. */
2334 if (DefaultUser->flags & USER_FLAG_NOPASS) {
2335 addReplyError(c,"AUTH <password> called without any password "
2336 "configured for the default user. Are you sure "
2337 "your configuration is correct?");
2338 return;
2339 }
2340
2341 username = shared.default_username;
2342 password = c->argv[1];
2343 } else {
2344 username = c->argv[1];
2345 password = c->argv[2];
2346 redactClientCommandArgument(c, 2);
2347 }
2348
2349 if (ACLAuthenticateUser(c,username,password) == C_OK) {
2350 addReply(c,shared.ok);
2351 } else {
2352 addReplyError(c,"-WRONGPASS invalid username-password pair or user is disabled.");
2353 }
2354 }
2355
2356 /* Set the password for the "default" ACL user. This implements supports for
2357 * requirepass config, so passing in NULL will set the user to be nopass. */
ACLUpdateDefaultUserPassword(sds password)2358 void ACLUpdateDefaultUserPassword(sds password) {
2359 ACLSetUser(DefaultUser,"resetpass",-1);
2360 if (password) {
2361 sds aclop = sdscatlen(sdsnew(">"), password, sdslen(password));
2362 ACLSetUser(DefaultUser,aclop,sdslen(aclop));
2363 sdsfree(aclop);
2364 } else {
2365 ACLSetUser(DefaultUser,"nopass",-1);
2366 }
2367 }
2368