aclcheck.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. #define REDISMODULE_EXPERIMENTAL_API
  2. #include "redismodule.h"
  3. #include <errno.h>
  4. #include <assert.h>
  5. /* A wrap for SET command with ACL check on the key. */
  6. int set_aclcheck_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  7. if (argc < 3) {
  8. return RedisModule_WrongArity(ctx);
  9. }
  10. /* Check that the key can be accessed */
  11. RedisModuleString *user_name = RedisModule_GetCurrentUserName(ctx);
  12. RedisModuleUser *user = RedisModule_GetModuleUserFromUserName(user_name);
  13. int ret = RedisModule_ACLCheckKeyPermissions(user, argv[1]);
  14. if (ret != 0) {
  15. RedisModule_ReplyWithError(ctx, "DENIED KEY");
  16. RedisModule_FreeModuleUser(user);
  17. RedisModule_FreeString(ctx, user_name);
  18. return REDISMODULE_OK;
  19. }
  20. RedisModuleCallReply *rep = RedisModule_Call(ctx, "SET", "v", argv + 1, argc - 1);
  21. if (!rep) {
  22. RedisModule_ReplyWithError(ctx, "NULL reply returned");
  23. } else {
  24. RedisModule_ReplyWithCallReply(ctx, rep);
  25. RedisModule_FreeCallReply(rep);
  26. }
  27. RedisModule_FreeModuleUser(user);
  28. RedisModule_FreeString(ctx, user_name);
  29. return REDISMODULE_OK;
  30. }
  31. /* A wrap for PUBLISH command with ACL check on the channel. */
  32. int publish_aclcheck_channel(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  33. if (argc != 3) {
  34. return RedisModule_WrongArity(ctx);
  35. }
  36. /* Check that the pubsub channel can be accessed */
  37. RedisModuleString *user_name = RedisModule_GetCurrentUserName(ctx);
  38. RedisModuleUser *user = RedisModule_GetModuleUserFromUserName(user_name);
  39. int ret = RedisModule_ACLCheckChannelPermissions(user, argv[1], 1);
  40. if (ret != 0) {
  41. RedisModule_ReplyWithError(ctx, "DENIED CHANNEL");
  42. RedisModule_FreeModuleUser(user);
  43. RedisModule_FreeString(ctx, user_name);
  44. return REDISMODULE_OK;
  45. }
  46. RedisModuleCallReply *rep = RedisModule_Call(ctx, "PUBLISH", "v", argv + 1, argc - 1);
  47. if (!rep) {
  48. RedisModule_ReplyWithError(ctx, "NULL reply returned");
  49. } else {
  50. RedisModule_ReplyWithCallReply(ctx, rep);
  51. RedisModule_FreeCallReply(rep);
  52. }
  53. RedisModule_FreeModuleUser(user);
  54. RedisModule_FreeString(ctx, user_name);
  55. return REDISMODULE_OK;
  56. }
  57. /* A wrap for RM_Call that check first that the command can be executed */
  58. int rm_call_aclcheck_cmd(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleString **argv, int argc) {
  59. if (argc < 2) {
  60. return RedisModule_WrongArity(ctx);
  61. }
  62. /* Check that the command can be executed */
  63. int ret = RedisModule_ACLCheckCommandPermissions(user, argv + 1, argc - 1);
  64. if (ret != 0) {
  65. RedisModule_ReplyWithError(ctx, "DENIED CMD");
  66. /* Add entry to ACL log */
  67. RedisModule_ACLAddLogEntry(ctx, user, argv[1]);
  68. return REDISMODULE_OK;
  69. }
  70. const char* cmd = RedisModule_StringPtrLen(argv[1], NULL);
  71. RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "v", argv + 2, argc - 2);
  72. if(!rep){
  73. RedisModule_ReplyWithError(ctx, "NULL reply returned");
  74. }else{
  75. RedisModule_ReplyWithCallReply(ctx, rep);
  76. RedisModule_FreeCallReply(rep);
  77. }
  78. return REDISMODULE_OK;
  79. }
  80. int rm_call_aclcheck_cmd_default_user(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  81. RedisModuleString *user_name = RedisModule_GetCurrentUserName(ctx);
  82. RedisModuleUser *user = RedisModule_GetModuleUserFromUserName(user_name);
  83. int res = rm_call_aclcheck_cmd(ctx, user, argv, argc);
  84. RedisModule_FreeModuleUser(user);
  85. RedisModule_FreeString(ctx, user_name);
  86. return res;
  87. }
  88. int rm_call_aclcheck_cmd_module_user(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  89. /* Create a user and authenticate */
  90. RedisModuleUser *user = RedisModule_CreateModuleUser("testuser1");
  91. RedisModule_SetModuleUserACL(user, "allcommands");
  92. RedisModule_SetModuleUserACL(user, "allkeys");
  93. RedisModule_SetModuleUserACL(user, "on");
  94. RedisModule_AuthenticateClientWithUser(ctx, user, NULL, NULL, NULL);
  95. int res = rm_call_aclcheck_cmd(ctx, user, argv, argc);
  96. /* authenticated back to "default" user (so once we free testuser1 we will not disconnected */
  97. RedisModule_AuthenticateClientWithACLUser(ctx, "default", 7, NULL, NULL, NULL);
  98. RedisModule_FreeModuleUser(user);
  99. return res;
  100. }
  101. /* A wrap for RM_Call that pass the 'C' flag to do ACL check on the command. */
  102. int rm_call_aclcheck(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
  103. REDISMODULE_NOT_USED(argv);
  104. REDISMODULE_NOT_USED(argc);
  105. if(argc < 2){
  106. return RedisModule_WrongArity(ctx);
  107. }
  108. const char* cmd = RedisModule_StringPtrLen(argv[1], NULL);
  109. RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "vC", argv + 2, argc - 2);
  110. if(!rep) {
  111. char err[100];
  112. switch (errno) {
  113. case EACCES:
  114. RedisModule_ReplyWithError(ctx, "ERR NOPERM");
  115. break;
  116. default:
  117. snprintf(err, sizeof(err) - 1, "ERR errno=%d", errno);
  118. RedisModule_ReplyWithError(ctx, err);
  119. break;
  120. }
  121. } else {
  122. RedisModule_ReplyWithCallReply(ctx, rep);
  123. RedisModule_FreeCallReply(rep);
  124. }
  125. return REDISMODULE_OK;
  126. }
  127. int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  128. REDISMODULE_NOT_USED(argv);
  129. REDISMODULE_NOT_USED(argc);
  130. if (RedisModule_Init(ctx,"aclcheck",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)
  131. return REDISMODULE_ERR;
  132. if (RedisModule_CreateCommand(ctx,"aclcheck.set.check.key", set_aclcheck_key,"",0,0,0) == REDISMODULE_ERR)
  133. return REDISMODULE_ERR;
  134. if (RedisModule_CreateCommand(ctx,"aclcheck.publish.check.channel", publish_aclcheck_channel,"",0,0,0) == REDISMODULE_ERR)
  135. return REDISMODULE_ERR;
  136. if (RedisModule_CreateCommand(ctx,"aclcheck.rm_call.check.cmd", rm_call_aclcheck_cmd_default_user,"",0,0,0) == REDISMODULE_ERR)
  137. return REDISMODULE_ERR;
  138. if (RedisModule_CreateCommand(ctx,"aclcheck.rm_call.check.cmd.module.user", rm_call_aclcheck_cmd_module_user,"",0,0,0) == REDISMODULE_ERR)
  139. return REDISMODULE_ERR;
  140. if (RedisModule_CreateCommand(ctx,"aclcheck.rm_call", rm_call_aclcheck,"",0,0,0) == REDISMODULE_ERR)
  141. return REDISMODULE_ERR;
  142. return REDISMODULE_OK;
  143. }