/prosody-0.8.2/plugins/mod_auth_internal_hashed.lua

# · Lua · 184 lines · 142 code · 28 blank · 14 comment · 38 complexity · 0716b796d3a2ce7e591ea108876ff9f6 MD5 · raw file

  1. -- Prosody IM
  2. -- Copyright (C) 2008-2010 Matthew Wild
  3. -- Copyright (C) 2008-2010 Waqas Hussain
  4. -- Copyright (C) 2010 Jeff Mitchell
  5. --
  6. -- This project is MIT/X11 licensed. Please see the
  7. -- COPYING file in the source package for more information.
  8. --
  9. local datamanager = require "util.datamanager";
  10. local log = require "util.logger".init("auth_internal_hashed");
  11. local type = type;
  12. local error = error;
  13. local ipairs = ipairs;
  14. local hashes = require "util.hashes";
  15. local jid_bare = require "util.jid".bare;
  16. local getAuthenticationDatabaseSHA1 = require "util.sasl.scram".getAuthenticationDatabaseSHA1;
  17. local config = require "core.configmanager";
  18. local usermanager = require "core.usermanager";
  19. local generate_uuid = require "util.uuid".generate;
  20. local new_sasl = require "util.sasl".new;
  21. local nodeprep = require "util.encodings".stringprep.nodeprep;
  22. local hosts = hosts;
  23. -- COMPAT w/old trunk: remove these two lines before 0.8 release
  24. local hmac_sha1 = require "util.hmac".sha1;
  25. local sha1 = require "util.hashes".sha1;
  26. local to_hex;
  27. do
  28. local function replace_byte_with_hex(byte)
  29. return ("%02x"):format(byte:byte());
  30. end
  31. function to_hex(binary_string)
  32. return binary_string:gsub(".", replace_byte_with_hex);
  33. end
  34. end
  35. local from_hex;
  36. do
  37. local function replace_hex_with_byte(hex)
  38. return string.char(tonumber(hex, 16));
  39. end
  40. function from_hex(hex_string)
  41. return hex_string:gsub("..", replace_hex_with_byte);
  42. end
  43. end
  44. local prosody = _G.prosody;
  45. -- Default; can be set per-user
  46. local iteration_count = 4096;
  47. function new_hashpass_provider(host)
  48. local provider = { name = "internal_hashed" };
  49. log("debug", "initializing hashpass authentication provider for host '%s'", host);
  50. function provider.test_password(username, password)
  51. local credentials = datamanager.load(username, host, "accounts") or {};
  52. if credentials.password ~= nil and string.len(credentials.password) ~= 0 then
  53. if credentials.password ~= password then
  54. return nil, "Auth failed. Provided password is incorrect.";
  55. end
  56. if provider.set_password(username, credentials.password) == nil then
  57. return nil, "Auth failed. Could not set hashed password from plaintext.";
  58. else
  59. return true;
  60. end
  61. end
  62. if credentials.iteration_count == nil or credentials.salt == nil or string.len(credentials.salt) == 0 then
  63. return nil, "Auth failed. Stored salt and iteration count information is not complete.";
  64. end
  65. -- convert hexpass to stored_key and server_key
  66. -- COMPAT w/old trunk: remove before 0.8 release
  67. if credentials.hashpass then
  68. local salted_password = from_hex(credentials.hashpass);
  69. credentials.stored_key = sha1(hmac_sha1(salted_password, "Client Key"), true);
  70. credentials.server_key = to_hex(hmac_sha1(salted_password, "Server Key"));
  71. credentials.hashpass = nil
  72. datamanager.store(username, host, "accounts", credentials);
  73. end
  74. local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, credentials.salt, credentials.iteration_count);
  75. local stored_key_hex = to_hex(stored_key);
  76. local server_key_hex = to_hex(server_key);
  77. if valid and stored_key_hex == credentials.stored_key and server_key_hex == credentials.server_key then
  78. return true;
  79. else
  80. return nil, "Auth failed. Invalid username, password, or password hash information.";
  81. end
  82. end
  83. function provider.set_password(username, password)
  84. local account = datamanager.load(username, host, "accounts");
  85. if account then
  86. account.salt = account.salt or generate_uuid();
  87. account.iteration_count = account.iteration_count or iteration_count;
  88. local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, account.salt, account.iteration_count);
  89. local stored_key_hex = to_hex(stored_key);
  90. local server_key_hex = to_hex(server_key);
  91. account.stored_key = stored_key_hex
  92. account.server_key = server_key_hex
  93. account.password = nil;
  94. return datamanager.store(username, host, "accounts", account);
  95. end
  96. return nil, "Account not available.";
  97. end
  98. function provider.user_exists(username)
  99. local account = datamanager.load(username, host, "accounts");
  100. if not account then
  101. log("debug", "account not found for username '%s' at host '%s'", username, module.host);
  102. return nil, "Auth failed. Invalid username";
  103. end
  104. return true;
  105. end
  106. function provider.create_user(username, password)
  107. if password == nil then
  108. return datamanager.store(username, host, "accounts", {});
  109. end
  110. local salt = generate_uuid();
  111. local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, salt, iteration_count);
  112. local stored_key_hex = to_hex(stored_key);
  113. local server_key_hex = to_hex(server_key);
  114. return datamanager.store(username, host, "accounts", {stored_key = stored_key_hex, server_key = server_key_hex, salt = salt, iteration_count = iteration_count});
  115. end
  116. function provider.delete_user(username)
  117. return datamanager.store(username, host, "accounts", nil);
  118. end
  119. function provider.get_sasl_handler()
  120. local testpass_authentication_profile = {
  121. plain_test = function(sasl, username, password, realm)
  122. local prepped_username = nodeprep(username);
  123. if not prepped_username then
  124. log("debug", "NODEprep failed on username: %s", username);
  125. return "", nil;
  126. end
  127. return usermanager.test_password(prepped_username, realm, password), true;
  128. end,
  129. scram_sha_1 = function(sasl, username, realm)
  130. local credentials = datamanager.load(username, host, "accounts");
  131. if not credentials then return; end
  132. if credentials.password then
  133. usermanager.set_password(username, credentials.password, host);
  134. credentials = datamanager.load(username, host, "accounts");
  135. if not credentials then return; end
  136. end
  137. -- convert hexpass to stored_key and server_key
  138. -- COMPAT w/old trunk: remove before 0.8 release
  139. if credentials.hashpass then
  140. local salted_password = from_hex(credentials.hashpass);
  141. credentials.stored_key = sha1(hmac_sha1(salted_password, "Client Key"), true);
  142. credentials.server_key = to_hex(hmac_sha1(salted_password, "Server Key"));
  143. credentials.hashpass = nil
  144. datamanager.store(username, host, "accounts", credentials);
  145. end
  146. local stored_key, server_key, iteration_count, salt = credentials.stored_key, credentials.server_key, credentials.iteration_count, credentials.salt;
  147. stored_key = stored_key and from_hex(stored_key);
  148. server_key = server_key and from_hex(server_key);
  149. return stored_key, server_key, iteration_count, salt, true;
  150. end
  151. };
  152. return new_sasl(module.host, testpass_authentication_profile);
  153. end
  154. return provider;
  155. end
  156. module:add_item("auth-provider", new_hashpass_provider(module.host));