mod_auth_internal_hashed: Use logger setup by moduleapi instead of going for util...
[prosody.git] / plugins / mod_auth_internal_hashed.lua
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
10 local getAuthenticationDatabaseSHA1 = require "util.sasl.scram".getAuthenticationDatabaseSHA1;
11 local usermanager = require "core.usermanager";
12 local generate_uuid = require "util.uuid".generate;
13 local new_sasl = require "util.sasl".new;
14
15 local log = module._log;
16
17 local accounts = module:open_store("accounts");
18
19 local to_hex;
20 do
21         local function replace_byte_with_hex(byte)
22                 return ("%02x"):format(byte:byte());
23         end
24         function to_hex(binary_string)
25                 return binary_string:gsub(".", replace_byte_with_hex);
26         end
27 end
28
29 local from_hex;
30 do
31         local function replace_hex_with_byte(hex)
32                 return string.char(tonumber(hex, 16));
33         end
34         function from_hex(hex_string)
35                 return hex_string:gsub("..", replace_hex_with_byte);
36         end
37 end
38
39
40 -- Default; can be set per-user
41 local iteration_count = 4096;
42
43 local host = module.host;
44 -- define auth provider
45 local provider = {};
46
47 function provider.test_password(username, password)
48         local credentials = accounts:get(username) or {};
49
50         if credentials.password ~= nil and string.len(credentials.password) ~= 0 then
51                 if credentials.password ~= password then
52                         return nil, "Auth failed. Provided password is incorrect.";
53                 end
54
55                 if provider.set_password(username, credentials.password) == nil then
56                         return nil, "Auth failed. Could not set hashed password from plaintext.";
57                 else
58                         return true;
59                 end
60         end
61
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
66         local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, credentials.salt, credentials.iteration_count);
67
68         local stored_key_hex = to_hex(stored_key);
69         local server_key_hex = to_hex(server_key);
70
71         if valid and stored_key_hex == credentials.stored_key and server_key_hex == credentials.server_key then
72                 return true;
73         else
74                 return nil, "Auth failed. Invalid username, password, or password hash information.";
75         end
76 end
77
78 function provider.set_password(username, password)
79         local account = accounts:get(username);
80         if account then
81                 account.salt = account.salt or generate_uuid();
82                 account.iteration_count = account.iteration_count or iteration_count;
83                 local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, account.salt, account.iteration_count);
84                 local stored_key_hex = to_hex(stored_key);
85                 local server_key_hex = to_hex(server_key);
86
87                 account.stored_key = stored_key_hex
88                 account.server_key = server_key_hex
89
90                 account.password = nil;
91                 return accounts:set(username, account);
92         end
93         return nil, "Account not available.";
94 end
95
96 function provider.user_exists(username)
97         local account = accounts:get(username);
98         if not account then
99                 log("debug", "account not found for username '%s' at host '%s'", username, host);
100                 return nil, "Auth failed. Invalid username";
101         end
102         return true;
103 end
104
105 function provider.users()
106         return accounts:users();
107 end
108
109 function provider.create_user(username, password)
110         if password == nil then
111                 return accounts:set(username, {});
112         end
113         local salt = generate_uuid();
114         local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, salt, iteration_count);
115         local stored_key_hex = to_hex(stored_key);
116         local server_key_hex = to_hex(server_key);
117         return accounts:set(username, {stored_key = stored_key_hex, server_key = server_key_hex, salt = salt, iteration_count = iteration_count});
118 end
119
120 function provider.delete_user(username)
121         return accounts:set(username, nil);
122 end
123
124 function provider.get_sasl_handler()
125         local testpass_authentication_profile = {
126                 plain_test = function(sasl, username, password, realm)
127                         return usermanager.test_password(username, realm, password), true;
128                 end,
129                 scram_sha_1 = function(sasl, username, realm)
130                         local credentials = accounts:get(username);
131                         if not credentials then return; end
132                         if credentials.password then
133                                 usermanager.set_password(username, credentials.password, host);
134                                 credentials = accounts:get(username);
135                                 if not credentials then return; end
136                         end
137
138                         local stored_key, server_key, iteration_count, salt = credentials.stored_key, credentials.server_key, credentials.iteration_count, credentials.salt;
139                         stored_key = stored_key and from_hex(stored_key);
140                         server_key = server_key and from_hex(server_key);
141                         return stored_key, server_key, iteration_count, salt, true;
142                 end
143         };
144         return new_sasl(host, testpass_authentication_profile);
145 end
146
147 module:provides("auth", provider);
148