mod_roster: When an user is deleted, unsubscribe from their contacts
[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 datamanager = require "util.datamanager";
11 local log = require "util.logger".init("auth_internal_hashed");
12 local getAuthenticationDatabaseSHA1 = require "util.sasl.scram".getAuthenticationDatabaseSHA1;
13 local usermanager = require "core.usermanager";
14 local generate_uuid = require "util.uuid".generate;
15 local new_sasl = require "util.sasl".new;
16 local nodeprep = require "util.encodings".stringprep.nodeprep;
17
18 local to_hex;
19 do
20         local function replace_byte_with_hex(byte)
21                 return ("%02x"):format(byte:byte());
22         end
23         function to_hex(binary_string)
24                 return binary_string:gsub(".", replace_byte_with_hex);
25         end
26 end
27
28 local from_hex;
29 do
30         local function replace_hex_with_byte(hex)
31                 return string.char(tonumber(hex, 16));
32         end
33         function from_hex(hex_string)
34                 return hex_string:gsub("..", replace_hex_with_byte);
35         end
36 end
37
38
39 -- Default; can be set per-user
40 local iteration_count = 4096;
41
42 function new_hashpass_provider(host)
43         local provider = { name = "internal_hashed" };
44         log("debug", "initializing internal_hashed authentication provider for host '%s'", host);
45
46         function provider.test_password(username, password)
47                 local credentials = datamanager.load(username, host, "accounts") or {};
48         
49                 if credentials.password ~= nil and string.len(credentials.password) ~= 0 then
50                         if credentials.password ~= password then
51                                 return nil, "Auth failed. Provided password is incorrect.";
52                         end
53
54                         if provider.set_password(username, credentials.password) == nil then
55                                 return nil, "Auth failed. Could not set hashed password from plaintext.";
56                         else
57                                 return true;
58                         end
59                 end
60
61                 if credentials.iteration_count == nil or credentials.salt == nil or string.len(credentials.salt) == 0 then
62                         return nil, "Auth failed. Stored salt and iteration count information is not complete.";
63                 end
64                 
65                 local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, credentials.salt, credentials.iteration_count);
66                 
67                 local stored_key_hex = to_hex(stored_key);
68                 local server_key_hex = to_hex(server_key);
69                 
70                 if valid and stored_key_hex == credentials.stored_key and server_key_hex == credentials.server_key then
71                         return true;
72                 else
73                         return nil, "Auth failed. Invalid username, password, or password hash information.";
74                 end
75         end
76
77         function provider.set_password(username, password)
78                 local account = datamanager.load(username, host, "accounts");
79                 if account then
80                         account.salt = account.salt or generate_uuid();
81                         account.iteration_count = account.iteration_count or iteration_count;
82                         local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, account.salt, account.iteration_count);
83                         local stored_key_hex = to_hex(stored_key);
84                         local server_key_hex = to_hex(server_key);
85                         
86                         account.stored_key = stored_key_hex
87                         account.server_key = server_key_hex
88
89                         account.password = nil;
90                         return datamanager.store(username, host, "accounts", account);
91                 end
92                 return nil, "Account not available.";
93         end
94
95         function provider.user_exists(username)
96                 local account = datamanager.load(username, host, "accounts");
97                 if not account then
98                         log("debug", "account not found for username '%s' at host '%s'", username, module.host);
99                         return nil, "Auth failed. Invalid username";
100                 end
101                 return true;
102         end
103
104         function provider.create_user(username, password)
105                 if password == nil then
106                         return datamanager.store(username, host, "accounts", {});
107                 end
108                 local salt = generate_uuid();
109                 local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, salt, iteration_count);
110                 local stored_key_hex = to_hex(stored_key);
111                 local server_key_hex = to_hex(server_key);
112                 return datamanager.store(username, host, "accounts", {stored_key = stored_key_hex, server_key = server_key_hex, salt = salt, iteration_count = iteration_count});
113         end
114
115         function provider.delete_user(username)
116                 return datamanager.store(username, host, "accounts", nil);
117         end
118
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                                 
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(module.host, testpass_authentication_profile);
145         end
146         
147         return provider;
148 end
149
150 module:add_item("auth-provider", new_hashpass_provider(module.host));
151