Bug Summary

File:src/acl.c
Warning:line 527, column 18
Assigned value is garbage or undefined

Annotated Source Code

Press '?' to see keyboard shortcuts

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