|
1 -- Prosody IM |
|
2 -- Copyright (C) 2008-2010 Matthew Wild |
|
3 -- Copyright (C) 2008-2010 Waqas Hussain |
|
4 -- Copyright (C) 2014 Kim Alvefur |
|
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 new_sasl = require "util.sasl".new; |
|
11 local random_bytes = require"util.random".bytes; |
|
12 local rfc5803 = require"util.rfc5803"; |
|
13 local scram = require "util.sasl.scram"; |
|
14 local hex = require"util.hex"; |
|
15 |
|
16 local mode = module:get_option_string("store_credentials", "hashed"); |
|
17 local default_i = module:get_option_number("default_iteration_count", 2^12); |
|
18 |
|
19 local accounts = module:open_store("accounts"); |
|
20 |
|
21 local function hashify(account) |
|
22 local i = default_i; |
|
23 local password = account.password; |
|
24 |
|
25 if account.hashedPassword then |
|
26 if not password then return end -- Already hashed |
|
27 local _, old_i = rfc5803.unpack(account.hashedPassword); |
|
28 if old_i > i then i = old_i; end |
|
29 end |
|
30 local salt = random_bytes(16); |
|
31 local ok, stored_key, server_key = scram.getAuthenticationDatabaseSHA1(password, salt, i); |
|
32 if not ok then return nil, stored_key; end |
|
33 account.hashedPassword = rfc5803.pack("SHA-1", i, salt, stored_key, server_key); |
|
34 account.password = nil; |
|
35 account.iteration_count, account.salt = nil, nil; |
|
36 account.stored_key, account.server_key = nil, nil; |
|
37 return account; |
|
38 end |
|
39 |
|
40 local function get_scram_hash(account) |
|
41 if account.hashedPassword then |
|
42 return rfc5803.unpack(account.hashedPassword); |
|
43 elseif account.stored_key then |
|
44 return "SHA-1", account.iteration_count, account.salt, |
|
45 hex.from(account.stored_key), hex.from(account.server_key); |
|
46 end |
|
47 end |
|
48 |
|
49 function user_exists(username) |
|
50 local account, err = accounts:get(username); |
|
51 if not account then return account, err; end |
|
52 return next(account) ~= nil; |
|
53 end |
|
54 |
|
55 function create_user(username, password) |
|
56 local account = { password = password }; |
|
57 if mode == "hashed" then |
|
58 hashify(account); |
|
59 end |
|
60 return accounts:set(username, account); |
|
61 end |
|
62 |
|
63 function delete_user(username) |
|
64 return accounts:set(username, nil); |
|
65 end |
|
66 |
|
67 function test_password(username, password) |
|
68 local account, err = accounts:get(username); |
|
69 if not account then return account, err; end |
|
70 if account.password then |
|
71 return password == account.password; |
|
72 end |
|
73 local hash, i, salt, our_stored_key, our_server_key = get_scram_hash(account); |
|
74 local ok, stored_key, server_key = scram.getAuthenticationDatabaseSHA1(password, salt, i); |
|
75 if not ok then return ok, stored_key; end |
|
76 ok = hash == "SHA-1" and stored_key == our_stored_key and server_key == our_server_key; |
|
77 if ok and mode == "unhash" then |
|
78 account.password, account.hashedPassword = password; |
|
79 accounts:set(username, account); |
|
80 end |
|
81 return ok; |
|
82 end |
|
83 |
|
84 local sasl_profile = {}; |
|
85 |
|
86 function get_sasl_handler() |
|
87 return new_sasl(module.host, sasl_profile); |
|
88 end |
|
89 |
|
90 function set_password(username, password) |
|
91 local account, err = accounts:get(username); |
|
92 if not account then account = {}; end |
|
93 account.password = password; |
|
94 if mode == "hashed" then |
|
95 account, err = hashify(account); |
|
96 end |
|
97 if not account then return account, err; end |
|
98 return accounts:set(username, account); |
|
99 end |
|
100 |
|
101 if mode == "plain" then |
|
102 function get_password(username) |
|
103 local account, err = accounts:get(username); |
|
104 if not account then return nil, err; end |
|
105 return account.password; |
|
106 end |
|
107 |
|
108 function sasl_profile:plain(username) |
|
109 return get_password(username), true; |
|
110 end |
|
111 elseif mode == "unhash" then |
|
112 function sasl_profile:plain_test(username, password) |
|
113 local ok = test_password(username, password); |
|
114 if ok then |
|
115 set_password(username, password); |
|
116 end |
|
117 return ok, true; |
|
118 end |
|
119 elseif mode == "hashed" then |
|
120 function sasl_profile:plain_test(username, password) |
|
121 return test_password(username, password), true; |
|
122 end |
|
123 |
|
124 function sasl_profile:scram_sha_1(username) |
|
125 local account, err = accounts:get(username); |
|
126 if not account then return account, err; end |
|
127 if not account.hashedPassword and hashify(account) then |
|
128 accounts:set(username, account); |
|
129 end |
|
130 local hash, i, salt, stored_key, server_key = get_scram_hash(account); |
|
131 if hash == "SHA-1" then |
|
132 return stored_key, server_key, i, salt, true; |
|
133 end |
|
134 end |
|
135 else |
|
136 module:log("error", "Unsupported store_credentials mode %q", mode); |
|
137 return; |
|
138 end |
|
139 |
|
140 if accounts.users then |
|
141 function users() |
|
142 return accounts:users(); |
|
143 end |
|
144 end |
|
145 |
|
146 module:provides "auth"; |
|
147 |