关键词搜索

源码搜索 ×
×

漫话Redis源码之八十七

发布2022-02-20浏览765次

详情内容

这个文件的函数比较杂,大致看看就行,以第一个为例,就是一下哈希相关的操作,为了密码安全。其余的函数了解用途就行。

  1. /* Given an SDS string, returns the SHA256 hex representation as a
  2. * new SDS string. */
  3. sds ACLHashPassword(unsigned char *cleartext, size_t len) {
  4. SHA256_CTX ctx;
  5. unsigned char hash[SHA256_BLOCK_SIZE];
  6. char hex[HASH_PASSWORD_LEN];
  7. char *cset = "0123456789abcdef";
  8. sha256_init(&ctx);
  9. sha256_update(&ctx,(unsigned char*)cleartext,len);
  10. sha256_final(&ctx,hash);
  11. for (int j = 0; j < SHA256_BLOCK_SIZE; j++) {
  12. hex[j*2] = cset[((hash[j]&0xF0)>>4)];
  13. hex[j*2+1] = cset[(hash[j]&0xF)];
  14. }
  15. return sdsnewlen(hex,HASH_PASSWORD_LEN);
  16. }
  17. /* Given a hash and the hash length, returns C_OK if it is a valid password
  18. * hash, or C_ERR otherwise. */
  19. int ACLCheckPasswordHash(unsigned char *hash, int hashlen) {
  20. if (hashlen != HASH_PASSWORD_LEN) {
  21. return C_ERR;
  22. }
  23. /* Password hashes can only be characters that represent
  24. * hexadecimal values, which are numbers and lowercase
  25. * characters 'a' through 'f'. */
  26. for(int i = 0; i < HASH_PASSWORD_LEN; i++) {
  27. char c = hash[i];
  28. if ((c < 'a' || c > 'f') && (c < '0' || c > '9')) {
  29. return C_ERR;
  30. }
  31. }
  32. return C_OK;
  33. }
  34. /* =============================================================================
  35. * Low level ACL API
  36. * ==========================================================================*/
  37. /* Return 1 if the specified string contains spaces or null characters.
  38. * We do this for usernames and key patterns for simpler rewriting of
  39. * ACL rules, presentation on ACL list, and to avoid subtle security bugs
  40. * that may arise from parsing the rules in presence of escapes.
  41. * The function returns 0 if the string has no spaces. */
  42. int ACLStringHasSpaces(const char *s, size_t len) {
  43. for (size_t i = 0; i < len; i++) {
  44. if (isspace(s[i]) || s[i] == 0) return 1;
  45. }
  46. return 0;
  47. }
  48. /* Given the category name the command returns the corresponding flag, or
  49. * zero if there is no match. */
  50. uint64_t ACLGetCommandCategoryFlagByName(const char *name) {
  51. for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {
  52. if (!strcasecmp(name,ACLCommandCategories[j].name)) {
  53. return ACLCommandCategories[j].flag;
  54. }
  55. }
  56. return 0; /* No match. */
  57. }
  58. /* Method for passwords/pattern comparison used for the user->passwords list
  59. * so that we can search for items with listSearchKey(). */
  60. int ACLListMatchSds(void *a, void *b) {
  61. return sdscmp(a,b) == 0;
  62. }
  63. /* Method to free list elements from ACL users password/patterns lists. */
  64. void ACLListFreeSds(void *item) {
  65. sdsfree(item);
  66. }
  67. /* Method to duplicate list elements from ACL users password/patterns lists. */
  68. void *ACLListDupSds(void *item) {
  69. return sdsdup(item);
  70. }
  71. /* Create a new user with the specified name, store it in the list
  72. * of users (the Users global radix tree), and returns a reference to
  73. * the structure representing the user.
  74. *
  75. * If the user with such name already exists NULL is returned. */
  76. user *ACLCreateUser(const char *name, size_t namelen) {
  77. if (raxFind(Users,(unsigned char*)name,namelen) != raxNotFound) return NULL;
  78. user *u = zmalloc(sizeof(*u));
  79. u->name = sdsnewlen(name,namelen);
  80. u->flags = USER_FLAG_DISABLED | server.acl_pubsub_default;
  81. u->allowed_subcommands = NULL;
  82. u->passwords = listCreate();
  83. u->patterns = listCreate();
  84. u->channels = listCreate();
  85. listSetMatchMethod(u->passwords,ACLListMatchSds);
  86. listSetFreeMethod(u->passwords,ACLListFreeSds);
  87. listSetDupMethod(u->passwords,ACLListDupSds);
  88. listSetMatchMethod(u->patterns,ACLListMatchSds);
  89. listSetFreeMethod(u->patterns,ACLListFreeSds);
  90. listSetDupMethod(u->patterns,ACLListDupSds);
  91. listSetMatchMethod(u->channels,ACLListMatchSds);
  92. listSetFreeMethod(u->channels,ACLListFreeSds);
  93. listSetDupMethod(u->channels,ACLListDupSds);
  94. memset(u->allowed_commands,0,sizeof(u->allowed_commands));
  95. raxInsert(Users,(unsigned char*)name,namelen,u,NULL);
  96. return u;
  97. }
  98. /* This function should be called when we need an unlinked "fake" user
  99. * we can use in order to validate ACL rules or for other similar reasons.
  100. * The user will not get linked to the Users radix tree. The returned
  101. * user should be released with ACLFreeUser() as usually. */
  102. user *ACLCreateUnlinkedUser(void) {
  103. char username[64];
  104. for (int j = 0; ; j++) {
  105. snprintf(username,sizeof(username),"__fakeuser:%d__",j);
  106. user *fakeuser = ACLCreateUser(username,strlen(username));
  107. if (fakeuser == NULL) continue;
  108. int retval = raxRemove(Users,(unsigned char*) username,
  109. strlen(username),NULL);
  110. serverAssert(retval != 0);
  111. return fakeuser;
  112. }
  113. }
  114. /* Release the memory used by the user structure. Note that this function
  115. * will not remove the user from the Users global radix tree. */
  116. void ACLFreeUser(user *u) {
  117. sdsfree(u->name);
  118. listRelease(u->passwords);
  119. listRelease(u->patterns);
  120. listRelease(u->channels);
  121. ACLResetSubcommands(u);
  122. zfree(u);
  123. }
  124. /* When a user is deleted we need to cycle the active
  125. * connections in order to kill all the pending ones that
  126. * are authenticated with such user. */
  127. void ACLFreeUserAndKillClients(user *u) {
  128. listIter li;
  129. listNode *ln;
  130. listRewind(server.clients,&li);
  131. while ((ln = listNext(&li)) != NULL) {
  132. client *c = listNodeValue(ln);
  133. if (c->user == u) {
  134. /* We'll free the connection asynchronously, so
  135. * in theory to set a different user is not needed.
  136. * However if there are bugs in Redis, soon or later
  137. * this may result in some security hole: it's much
  138. * more defensive to set the default user and put
  139. * it in non authenticated mode. */
  140. c->user = DefaultUser;
  141. c->authenticated = 0;
  142. /* We will write replies to this client later, so we can't
  143. * close it directly even if async. */
  144. if (c == server.current_client) {
  145. c->flags |= CLIENT_CLOSE_AFTER_COMMAND;
  146. } else {
  147. freeClientAsync(c);
  148. }
  149. }
  150. }
  151. ACLFreeUser(u);
  152. }
  153. /* Copy the user ACL rules from the source user 'src' to the destination
  154. * user 'dst' so that at the end of the process they'll have exactly the
  155. * same rules (but the names will continue to be the original ones). */
  156. void ACLCopyUser(user *dst, user *src) {
  157. listRelease(dst->passwords);
  158. listRelease(dst->patterns);
  159. listRelease(dst->channels);
  160. dst->passwords = listDup(src->passwords);
  161. dst->patterns = listDup(src->patterns);
  162. dst->channels = listDup(src->channels);
  163. memcpy(dst->allowed_commands,src->allowed_commands,
  164. sizeof(dst->allowed_commands));
  165. dst->flags = src->flags;
  166. ACLResetSubcommands(dst);
  167. /* Copy the allowed subcommands array of array of SDS strings. */
  168. if (src->allowed_subcommands) {
  169. for (int j = 0; j < USER_COMMAND_BITS_COUNT; j++) {
  170. if (src->allowed_subcommands[j]) {
  171. for (int i = 0; src->allowed_subcommands[j][i]; i++)
  172. {
  173. ACLAddAllowedSubcommand(dst, j,
  174. src->allowed_subcommands[j][i]);
  175. }
  176. }
  177. }
  178. }
  179. }
  180. /* Free all the users registered in the radix tree 'users' and free the
  181. * radix tree itself. */
  182. void ACLFreeUsersSet(rax *users) {
  183. raxFreeWithCallback(users,(void(*)(void*))ACLFreeUserAndKillClients);
  184. }
  185. /* Given a command ID, this function set by reference 'word' and 'bit'
  186. * so that user->allowed_commands[word] will address the right word
  187. * where the corresponding bit for the provided ID is stored, and
  188. * so that user->allowed_commands[word]&bit will identify that specific
  189. * bit. The function returns C_ERR in case the specified ID overflows
  190. * the bitmap in the user representation. */
  191. int ACLGetCommandBitCoordinates(uint64_t id, uint64_t *word, uint64_t *bit) {
  192. if (id >= USER_COMMAND_BITS_COUNT) return C_ERR;
  193. *word = id / sizeof(uint64_t) / 8;
  194. *bit = 1ULL << (id % (sizeof(uint64_t) * 8));
  195. return C_OK;
  196. }
  197. /* Check if the specified command bit is set for the specified user.
  198. * The function returns 1 is the bit is set or 0 if it is not.
  199. * Note that this function does not check the ALLCOMMANDS flag of the user
  200. * but just the lowlevel bitmask.
  201. *
  202. * If the bit overflows the user internal representation, zero is returned
  203. * in order to disallow the execution of the command in such edge case. */
  204. int ACLGetUserCommandBit(user *u, unsigned long id) {
  205. uint64_t word, bit;
  206. if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return 0;
  207. return (u->allowed_commands[word] & bit) != 0;
  208. }
  209. /* When +@all or allcommands is given, we set a reserved bit as well that we
  210. * can later test, to see if the user has the right to execute "future commands",
  211. * that is, commands loaded later via modules. */
  212. int ACLUserCanExecuteFutureCommands(user *u) {
  213. return ACLGetUserCommandBit(u,USER_COMMAND_BITS_COUNT-1);
  214. }
  215. /* Set the specified command bit for the specified user to 'value' (0 or 1).
  216. * If the bit overflows the user internal representation, no operation
  217. * is performed. As a side effect of calling this function with a value of
  218. * zero, the user flag ALLCOMMANDS is cleared since it is no longer possible
  219. * to skip the command bit explicit test. */
  220. void ACLSetUserCommandBit(user *u, unsigned long id, int value) {
  221. uint64_t word, bit;
  222. if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return;
  223. if (value) {
  224. u->allowed_commands[word] |= bit;
  225. } else {
  226. u->allowed_commands[word] &= ~bit;
  227. u->flags &= ~USER_FLAG_ALLCOMMANDS;
  228. }
  229. }
  230. /* This is like ACLSetUserCommandBit(), but instead of setting the specified
  231. * ID, it will check all the commands in the category specified as argument,
  232. * and will set all the bits corresponding to such commands to the specified
  233. * value. Since the category passed by the user may be non existing, the
  234. * function returns C_ERR if the category was not found, or C_OK if it was
  235. * found and the operation was performed. */
  236. int ACLSetUserCommandBitsForCategory(user *u, const char *category, int value) {
  237. uint64_t cflag = ACLGetCommandCategoryFlagByName(category);
  238. if (!cflag) return C_ERR;
  239. dictIterator *di = dictGetIterator(server.orig_commands);
  240. dictEntry *de;
  241. while ((de = dictNext(di)) != NULL) {
  242. struct redisCommand *cmd = dictGetVal(de);
  243. if (cmd->flags & CMD_MODULE) continue; /* Ignore modules commands. */
  244. if (cmd->flags & cflag) {
  245. ACLSetUserCommandBit(u,cmd->id,value);
  246. ACLResetSubcommandsForCommand(u,cmd->id);
  247. }
  248. }
  249. dictReleaseIterator(di);
  250. return C_OK;
  251. }
  252. /* Return the number of commands allowed (on) and denied (off) for the user 'u'
  253. * in the subset of commands flagged with the specified category name.
  254. * If the category name is not valid, C_ERR is returned, otherwise C_OK is
  255. * returned and on and off are populated by reference. */
  256. int ACLCountCategoryBitsForUser(user *u, unsigned long *on, unsigned long *off,
  257. const char *category)
  258. {
  259. uint64_t cflag = ACLGetCommandCategoryFlagByName(category);
  260. if (!cflag) return C_ERR;
  261. *on = *off = 0;
  262. dictIterator *di = dictGetIterator(server.orig_commands);
  263. dictEntry *de;
  264. while ((de = dictNext(di)) != NULL) {
  265. struct redisCommand *cmd = dictGetVal(de);
  266. if (cmd->flags & cflag) {
  267. if (ACLGetUserCommandBit(u,cmd->id))
  268. (*on)++;
  269. else
  270. (*off)++;
  271. }
  272. }
  273. dictReleaseIterator(di);
  274. return C_OK;
  275. }
  276. /* This function returns an SDS string representing the specified user ACL
  277. * rules related to command execution, in the same format you could set them
  278. * back using ACL SETUSER. The function will return just the set of rules needed
  279. * to recreate the user commands bitmap, without including other user flags such
  280. * as on/off, passwords and so forth. The returned string always starts with
  281. * the +@all or -@all rule, depending on the user bitmap, and is followed, if
  282. * needed, by the other rules needed to narrow or extend what the user can do. */
  283. sds ACLDescribeUserCommandRules(user *u) {
  284. sds rules = sdsempty();
  285. int additive; /* If true we start from -@all and add, otherwise if
  286. false we start from +@all and remove. */
  287. /* This code is based on a trick: as we generate the rules, we apply
  288. * them to a fake user, so that as we go we still know what are the
  289. * bit differences we should try to address by emitting more rules. */
  290. user fu = {0};
  291. user *fakeuser = &fu;
  292. /* Here we want to understand if we should start with +@all and remove
  293. * the commands corresponding to the bits that are not set in the user
  294. * commands bitmap, or the contrary. Note that semantically the two are
  295. * different. For instance starting with +@all and subtracting, the user
  296. * will be able to execute future commands, while -@all and adding will just
  297. * allow the user the run the selected commands and/or categories.
  298. * How do we test for that? We use the trick of a reserved command ID bit
  299. * that is set only by +@all (and its alias "allcommands"). */
  300. if (ACLUserCanExecuteFutureCommands(u)) {
  301. additive = 0;
  302. rules = sdscat(rules,"+@all ");
  303. ACLSetUser(fakeuser,"+@all",-1);
  304. } else {
  305. additive = 1;
  306. rules = sdscat(rules,"-@all ");
  307. ACLSetUser(fakeuser,"-@all",-1);
  308. }
  309. /* Attempt to find a good approximation for categories and commands
  310. * based on the current bits used, by looping over the category list
  311. * and applying the best fit each time. Often a set of categories will not
  312. * perfectly match the set of commands into it, so at the end we do a
  313. * final pass adding/removing the single commands needed to make the bitmap
  314. * exactly match. A temp user is maintained to keep track of categories
  315. * already applied. */
  316. user tu = {0};
  317. user *tempuser = &tu;
  318. /* Keep track of the categories that have been applied, to prevent
  319. * applying them twice. */
  320. char applied[sizeof(ACLCommandCategories)/sizeof(ACLCommandCategories[0])];
  321. memset(applied, 0, sizeof(applied));
  322. memcpy(tempuser->allowed_commands,
  323. u->allowed_commands,
  324. sizeof(u->allowed_commands));
  325. while (1) {
  326. int best = -1;
  327. unsigned long mindiff = INT_MAX, maxsame = 0;
  328. for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {
  329. if (applied[j]) continue;
  330. unsigned long on, off, diff, same;
  331. ACLCountCategoryBitsForUser(tempuser,&on,&off,ACLCommandCategories[j].name);
  332. /* Check if the current category is the best this loop:
  333. * * It has more commands in common with the user than commands
  334. * that are different.
  335. * AND EITHER
  336. * * It has the fewest number of differences
  337. * than the best match we have found so far.
  338. * * OR it matches the fewest number of differences
  339. * that we've seen but it has more in common. */
  340. diff = additive ? off : on;
  341. same = additive ? on : off;
  342. if (same > diff &&
  343. ((diff < mindiff) || (diff == mindiff && same > maxsame)))
  344. {
  345. best = j;
  346. mindiff = diff;
  347. maxsame = same;
  348. }
  349. }
  350. /* We didn't find a match */
  351. if (best == -1) break;
  352. sds op = sdsnewlen(additive ? "+@" : "-@", 2);
  353. op = sdscat(op,ACLCommandCategories[best].name);
  354. ACLSetUser(fakeuser,op,-1);
  355. sds invop = sdsnewlen(additive ? "-@" : "+@", 2);
  356. invop = sdscat(invop,ACLCommandCategories[best].name);
  357. ACLSetUser(tempuser,invop,-1);
  358. rules = sdscatsds(rules,op);
  359. rules = sdscatlen(rules," ",1);
  360. sdsfree(op);
  361. sdsfree(invop);
  362. applied[best] = 1;
  363. }
  364. /* Fix the final ACLs with single commands differences. */
  365. dictIterator *di = dictGetIterator(server.orig_commands);
  366. dictEntry *de;
  367. while ((de = dictNext(di)) != NULL) {
  368. struct redisCommand *cmd = dictGetVal(de);
  369. int userbit = ACLGetUserCommandBit(u,cmd->id);
  370. int fakebit = ACLGetUserCommandBit(fakeuser,cmd->id);
  371. if (userbit != fakebit) {
  372. rules = sdscatlen(rules, userbit ? "+" : "-", 1);
  373. rules = sdscat(rules,cmd->name);
  374. rules = sdscatlen(rules," ",1);
  375. ACLSetUserCommandBit(fakeuser,cmd->id,userbit);
  376. }
  377. /* Emit the subcommands if there are any. */
  378. if (userbit == 0 && u->allowed_subcommands &&
  379. u->allowed_subcommands[cmd->id])
  380. {
  381. for (int j = 0; u->allowed_subcommands[cmd->id][j]; j++) {
  382. rules = sdscatlen(rules,"+",1);
  383. rules = sdscat(rules,cmd->name);
  384. rules = sdscatlen(rules,"|",1);
  385. rules = sdscatsds(rules,u->allowed_subcommands[cmd->id][j]);
  386. rules = sdscatlen(rules," ",1);
  387. }
  388. }
  389. }
  390. dictReleaseIterator(di);
  391. /* Trim the final useless space. */
  392. sdsrange(rules,0,-2);
  393. /* This is technically not needed, but we want to verify that now the
  394. * predicted bitmap is exactly the same as the user bitmap, and abort
  395. * otherwise, because aborting is better than a security risk in this
  396. * code path. */
  397. if (memcmp(fakeuser->allowed_commands,
  398. u->allowed_commands,
  399. sizeof(u->allowed_commands)) != 0)
  400. {
  401. serverLog(LL_WARNING,
  402. "CRITICAL ERROR: User ACLs don't match final bitmap: '%s'",
  403. rules);
  404. serverPanic("No bitmap match in ACLDescribeUserCommandRules()");
  405. }
  406. return rules;
  407. }
  408. /* This is similar to ACLDescribeUserCommandRules(), however instead of
  409. * describing just the user command rules, everything is described: user
  410. * flags, keys, passwords and finally the command rules obtained via
  411. * the ACLDescribeUserCommandRules() function. This is the function we call
  412. * when we want to rewrite the configuration files describing ACLs and
  413. * in order to show users with ACL LIST. */
  414. sds ACLDescribeUser(user *u) {
  415. sds res = sdsempty();
  416. /* Flags. */
  417. for (int j = 0; ACLUserFlags[j].flag; j++) {
  418. /* Skip the allcommands, allkeys and allchannels flags because they'll
  419. * be emitted later as +@all, ~* and &*. */
  420. if (ACLUserFlags[j].flag == USER_FLAG_ALLKEYS ||
  421. ACLUserFlags[j].flag == USER_FLAG_ALLCHANNELS ||
  422. ACLUserFlags[j].flag == USER_FLAG_ALLCOMMANDS) continue;
  423. if (u->flags & ACLUserFlags[j].flag) {
  424. res = sdscat(res,ACLUserFlags[j].name);
  425. res = sdscatlen(res," ",1);
  426. }
  427. }
  428. /* Passwords. */
  429. listIter li;
  430. listNode *ln;
  431. listRewind(u->passwords,&li);
  432. while((ln = listNext(&li))) {
  433. sds thispass = listNodeValue(ln);
  434. res = sdscatlen(res,"#",1);
  435. res = sdscatsds(res,thispass);
  436. res = sdscatlen(res," ",1);
  437. }
  438. /* Key patterns. */
  439. if (u->flags & USER_FLAG_ALLKEYS) {
  440. res = sdscatlen(res,"~* ",3);
  441. } else {
  442. listRewind(u->patterns,&li);
  443. while((ln = listNext(&li))) {
  444. sds thispat = listNodeValue(ln);
  445. res = sdscatlen(res,"~",1);
  446. res = sdscatsds(res,thispat);
  447. res = sdscatlen(res," ",1);
  448. }
  449. }
  450. /* Pub/sub channel patterns. */
  451. if (u->flags & USER_FLAG_ALLCHANNELS) {
  452. res = sdscatlen(res,"&* ",3);
  453. } else {
  454. res = sdscatlen(res,"resetchannels ",14);
  455. listRewind(u->channels,&li);
  456. while((ln = listNext(&li))) {
  457. sds thispat = listNodeValue(ln);
  458. res = sdscatlen(res,"&",1);
  459. res = sdscatsds(res,thispat);
  460. res = sdscatlen(res," ",1);
  461. }
  462. }
  463. /* Command rules. */
  464. sds rules = ACLDescribeUserCommandRules(u);
  465. res = sdscatsds(res,rules);
  466. sdsfree(rules);
  467. return res;
  468. }

相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载