2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 -- Copyright (C) 2010 Jeff Mitchell
6 -- This project is MIT/X11 licensed. Please see the
7 -- COPYING file in the source package for more information.
12 local scram_hashers
= require
"util.sasl.scram".hashers
;
13 local usermanager
= require
"core.usermanager";
14 local generate_uuid
= require
"util.uuid".generate
;
15 local new_sasl
= require
"util.sasl".new
;
16 local hex
= require
"util.hex";
17 local to_hex
, from_hex
= hex
.to
, hex
.from
;
19 local log = module
._log
;
20 local host
= module
.host
;
22 local accounts
= module
:open_store("accounts");
24 local hash_name
= module
:get_option_string("password_hash", "SHA-1");
25 local get_auth_db
= assert(scram_hashers
[hash_name
], "SCRAM-"..hash_name
.." not supported by SASL library");
26 local scram_name
= "scram_"..hash_name
:gsub("%-","_"):lower();
28 -- Default; can be set per-user
29 local default_iteration_count
= 4096;
31 -- define auth provider
34 function provider
.test_password(username
, password
)
35 log("debug", "test password for user '%s'", username
);
36 local credentials
= accounts
:get(username
) or {};
38 if credentials
.password
~= nil and string.len(credentials
.password
) ~= 0 then
39 if credentials
.password
~= password
then
40 return nil, "Auth failed. Provided password is incorrect.";
43 if provider
.set_password(username
, credentials
.password
) == nil then
44 return nil, "Auth failed. Could not set hashed password from plaintext.";
50 if credentials
.iteration_count
== nil or credentials
.salt
== nil or string.len(credentials
.salt
) == 0 then
51 return nil, "Auth failed. Stored salt and iteration count information is not complete.";
54 local valid
, stored_key
, server_key
= get_auth_db(password
, credentials
.salt
, credentials
.iteration_count
);
56 local stored_key_hex
= to_hex(stored_key
);
57 local server_key_hex
= to_hex(server_key
);
59 if valid
and stored_key_hex
== credentials
.stored_key
and server_key_hex
== credentials
.server_key
then
62 return nil, "Auth failed. Invalid username, password, or password hash information.";
66 function provider
.set_password(username
, password
)
67 log("debug", "set_password for username '%s'", username
);
68 local account
= accounts
:get(username
);
70 account
.salt
= generate_uuid();
71 account
.iteration_count
= max(account
.iteration_count
or 0, default_iteration_count
);
72 local valid
, stored_key
, server_key
= get_auth_db(password
, account
.salt
, account
.iteration_count
);
73 local stored_key_hex
= to_hex(stored_key
);
74 local server_key_hex
= to_hex(server_key
);
76 account
.stored_key
= stored_key_hex
77 account
.server_key
= server_key_hex
79 account
.password
= nil;
80 return accounts
:set(username
, account
);
82 return nil, "Account not available.";
85 function provider
.user_exists(username
)
86 local account
= accounts
:get(username
);
88 log("debug", "account not found for username '%s'", username
);
89 return nil, "Auth failed. Invalid username";
94 function provider
.users()
95 return accounts
:users();
98 function provider
.create_user(username
, password
)
99 if password
== nil then
100 return accounts
:set(username
, {});
102 local salt
= generate_uuid();
103 local valid
, stored_key
, server_key
= get_auth_db(password
, salt
, default_iteration_count
);
104 local stored_key_hex
= to_hex(stored_key
);
105 local server_key_hex
= to_hex(server_key
);
106 return accounts
:set(username
, {
107 stored_key
= stored_key_hex
, server_key
= server_key_hex
,
108 salt
= salt
, iteration_count
= default_iteration_count
112 function provider
.delete_user(username
)
113 return accounts
:set(username
, nil);
116 function provider
.get_sasl_handler()
117 local testpass_authentication_profile
= {
118 plain_test
= function(_
, username
, password
, realm
)
119 return usermanager
.test_password(username
, realm
, password
), true;
121 [scram_name
] = function(_
, username
)
122 local credentials
= accounts
:get(username
);
123 if not credentials
then return; end
124 if credentials
.password
then
125 if provider
.set_password(username
, credentials
.password
) == nil then
126 return nil, "Auth failed. Could not set hashed password from plaintext.";
128 credentials
= accounts
:get(username
);
129 if not credentials
then return; end
132 local stored_key
, server_key
= credentials
.stored_key
, credentials
.server_key
;
133 local iteration_count
, salt
= credentials
.iteration_count
, credentials
.salt
;
134 stored_key
= stored_key
and from_hex(stored_key
);
135 server_key
= server_key
and from_hex(server_key
);
136 return stored_key
, server_key
, iteration_count
, salt
, true;
139 return new_sasl(host
, testpass_authentication_profile
);
142 module
:provides("auth", provider
);