Skip to content

Commit 93b5a3a

Browse files
Introduce per-user hashing functions, default to SHA-256
References #270.
1 parent 2eec791 commit 93b5a3a

10 files changed

+194
-22
lines changed

ebin/rabbit_app.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
{default_vhost, <<"/">>},
3939
{default_permissions, [<<".*">>, <<".*">>, <<".*">>]},
4040
{loopback_users, [<<"guest">>]},
41+
{password_hashing_mod, rabbit_password_hashing_sha256},
4142
{cluster_nodes, {[], disc}},
4243
{server_properties, []},
4344
{collect_statistics, none},

include/rabbit.hrl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@
2727
-record(authz_socket_info, {sockname, peername}).
2828

2929
%% Implementation for the internal auth backend
30-
-record(internal_user, {username, password_hash, tags}).
30+
-record(internal_user, {
31+
username,
32+
password_hash,
33+
tags,
34+
hashing_algorithm}).
3135
-record(permission, {configure, write, read}).
3236
-record(user_vhost, {username, virtual_host}).
3337
-record(user_permission, {user_vhost, permission}).

src/rabbit_auth_backend_internal.erl

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
list_user_permissions/1, list_vhost_permissions/1,
3535
list_user_vhost_permissions/2]).
3636

37+
%% for testing
38+
-export([hashing_module_for_user/1]).
39+
3740
%%----------------------------------------------------------------------------
3841

3942
-ifdef(use_specs).
@@ -77,13 +80,24 @@
7780
%%----------------------------------------------------------------------------
7881
%% Implementation of rabbit_auth_backend
7982

83+
%% Returns a password hashing module for
84+
%% the user record provided. If there is no
85+
%% information in the record, we consider
86+
%% it to be legacy (inserted by a version older than
87+
%% 3.6.0) and fall back to MD5, the now obsolete
88+
%% hashing function.
89+
hashing_module_for_user(#internal_user{
90+
hashing_algorithm = ModOrUndefined}) ->
91+
rabbit_password:hashing_mod(ModOrUndefined).
92+
8093
user_login_authentication(Username, []) ->
8194
internal_check_user_login(Username, fun(_) -> true end);
8295
user_login_authentication(Username, [{password, Cleartext}]) ->
8396
internal_check_user_login(
8497
Username,
85-
fun (#internal_user{password_hash = <<Salt:4/binary, Hash/binary>>}) ->
86-
Hash =:= salted_md5(Salt, Cleartext);
98+
fun (#internal_user{password_hash = <<Salt:4/binary, Hash/binary>>} = U) ->
99+
Hash =:= rabbit_password:salted_hash(
100+
hashing_module_for_user(U), Salt, Cleartext);
87101
(#internal_user{}) ->
88102
false
89103
end);
@@ -147,17 +161,20 @@ permission_index(read) -> #permission.read.
147161

148162
add_user(Username, Password) ->
149163
rabbit_log:info("Creating user '~s'~n", [Username]),
164+
%% hash_password will pick the hashing
165+
%% function configured for us but we
166+
%% also need to store a hint as part of the
167+
%% record, so we retrieve it here one more time
168+
HashingMod = rabbit_password:hashing_mod(),
169+
User = #internal_user{username = Username,
170+
password_hash = hash_password(Password),
171+
tags = [],
172+
hashing_algorithm = HashingMod},
150173
R = rabbit_misc:execute_mnesia_transaction(
151174
fun () ->
152175
case mnesia:wread({rabbit_user, Username}) of
153176
[] ->
154-
ok = mnesia:write(
155-
rabbit_user,
156-
#internal_user{username = Username,
157-
password_hash =
158-
hash_password(Password),
159-
tags = []},
160-
write);
177+
ok = mnesia:write(rabbit_user, User, write);
161178
_ ->
162179
mnesia:abort({user_already_exists, Username})
163180
end
@@ -202,24 +219,14 @@ clear_password(Username) ->
202219
R.
203220

204221
hash_password(Cleartext) ->
205-
random:seed(erlang:phash2([node()]),
206-
time_compat:monotonic_time(),
207-
time_compat:unique_integer()),
208-
Salt = random:uniform(16#ffffffff),
209-
SaltBin = <<Salt:32>>,
210-
Hash = salted_md5(SaltBin, Cleartext),
211-
<<SaltBin/binary, Hash/binary>>.
222+
rabbit_password:hash(Cleartext).
212223

213224
change_password_hash(Username, PasswordHash) ->
214225
update_user(Username, fun(User) ->
215226
User#internal_user{
216227
password_hash = PasswordHash }
217228
end).
218229

219-
salted_md5(Salt, Cleartext) ->
220-
Salted = <<Salt/binary, Cleartext/binary>>,
221-
erlang:md5(Salted).
222-
223230
set_tags(Username, Tags) ->
224231
rabbit_log:info("Setting user tags for user '~s' to ~p~n",
225232
[Username, Tags]),

src/rabbit_password.erl

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
%% The contents of this file are subject to the Mozilla Public License
2+
%% Version 1.1 (the "License"); you may not use this file except in
3+
%% compliance with the License. You may obtain a copy of the License
4+
%% at http://www.mozilla.org/MPL/
5+
%%
6+
%% Software distributed under the License is distributed on an "AS IS"
7+
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
8+
%% the License for the specific language governing rights and
9+
%% limitations under the License.
10+
%%
11+
%% The Original Code is RabbitMQ.
12+
%%
13+
%% The Initial Developer of the Original Code is GoPivotal, Inc.
14+
%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
15+
%%
16+
-module(rabbit_password).
17+
-include("rabbit.hrl").
18+
19+
-define(DEFAULT_HASHING_MODULE, rabbit_password_hashing_sha256).
20+
21+
%%
22+
%% API
23+
%%
24+
25+
-export([hash/1, generate_salt/0, salted_hash/2, salted_hash/3,
26+
hashing_mod/0, hashing_mod/1]).
27+
28+
hash(Cleartext) ->
29+
SaltBin = generate_salt(),
30+
Hash = salted_hash(SaltBin, Cleartext),
31+
<<SaltBin/binary, Hash/binary>>.
32+
33+
generate_salt() ->
34+
random:seed(erlang:phash2([node()]),
35+
time_compat:monotonic_time(),
36+
time_compat:unique_integer()),
37+
Salt = random:uniform(16#ffffffff),
38+
<<Salt:32>>.
39+
40+
salted_hash(Salt, Cleartext) ->
41+
salted_hash(hashing_mod(), Salt, Cleartext).
42+
43+
salted_hash(Mod, Salt, Cleartext) ->
44+
Fun = fun Mod:hash/1,
45+
Fun(<<Salt/binary, Cleartext/binary>>).
46+
47+
hashing_mod() ->
48+
rabbit_misc:get_env(rabbit, password_hashing_mod, ?DEFAULT_HASHING_MODULE).
49+
50+
hashing_mod(rabbit_password_hashing_sha256) ->
51+
rabbit_password_hashing_sha256;
52+
hashing_mod(rabbit_password_hashing_md5) ->
53+
rabbit_password_hashing_md5;
54+
%% fall back to the hashing function that's been
55+
%% used prior to 3.6.0
56+
hashing_mod(undefined) ->
57+
rabbit_password_hashing_md5;
58+
%% if a custom module is configured,
59+
%% simply use it
60+
hashing_mod(CustomMod) when is_atom(CustomMod) ->
61+
CustomMod.

src/rabbit_password_hashing.erl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
%% The contents of this file are subject to the Mozilla Public License
2+
%% Version 1.1 (the "License"); you may not use this file except in
3+
%% compliance with the License. You may obtain a copy of the License
4+
%% at http://www.mozilla.org/MPL/
5+
%%
6+
%% Software distributed under the License is distributed on an "AS IS"
7+
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
8+
%% the License for the specific language governing rights and
9+
%% limitations under the License.
10+
%%
11+
%% The Original Code is RabbitMQ.
12+
%%
13+
%% The Initial Developer of the Original Code is GoPivotal, Inc.
14+
%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
15+
%%
16+
-module(rabbit_password_hashing).
17+
-include("rabbit.hrl").
18+
19+
-ifdef(use_specs).
20+
21+
-callback hash(rabbit_types:password()) -> rabbit_types:password_hash().
22+
23+
-else.
24+
25+
-export([behaviour_info/1, ]).
26+
27+
behaviour_info(callbacks) ->
28+
[{hash, 1}];
29+
behaviour_info(_Other) ->
30+
undefined.
31+
32+
-endif.

src/rabbit_password_hashing_md5.erl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
%% The contents of this file are subject to the Mozilla Public License
2+
%% Version 1.1 (the "License"); you may not use this file except in
3+
%% compliance with the License. You may obtain a copy of the License
4+
%% at http://www.mozilla.org/MPL/
5+
%%
6+
%% Software distributed under the License is distributed on an "AS IS"
7+
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
8+
%% the License for the specific language governing rights and
9+
%% limitations under the License.
10+
%%
11+
%% The Original Code is RabbitMQ.
12+
%%
13+
%% The Initial Developer of the Original Code is GoPivotal, Inc.
14+
%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
15+
%%
16+
17+
%% Legacy hashing implementation, only used as the
18+
%% last resort when #internal_user.hashing_algorithm
19+
%% is md5 or undefined (the case in pre-3.6.0 user records).
20+
-module(rabbit_password_hashing_md5).
21+
22+
-behaviour(rabbit_password_hashing).
23+
24+
-export([hash/1]).
25+
26+
hash(Binary) ->
27+
erlang:md5(Binary).
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
%% The contents of this file are subject to the Mozilla Public License
2+
%% Version 1.1 (the "License"); you may not use this file except in
3+
%% compliance with the License. You may obtain a copy of the License
4+
%% at http://www.mozilla.org/MPL/
5+
%%
6+
%% Software distributed under the License is distributed on an "AS IS"
7+
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
8+
%% the License for the specific language governing rights and
9+
%% limitations under the License.
10+
%%
11+
%% The Original Code is RabbitMQ.
12+
%%
13+
%% The Initial Developer of the Original Code is GoPivotal, Inc.
14+
%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
15+
%%
16+
17+
-module(rabbit_password_hashing_sha256).
18+
19+
-behaviour(rabbit_password_hashing).
20+
21+
-export([hash/1]).
22+
23+
hash(Binary) ->
24+
crypto:hash(sha256, Binary).

src/rabbit_registry.erl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ class_module(queue_decorator) -> rabbit_queue_decorator;
134134
class_module(policy_validator) -> rabbit_policy_validator;
135135
class_module(ha_mode) -> rabbit_mirror_queue_mode;
136136
class_module(channel_interceptor) -> rabbit_channel_interceptor;
137-
class_module(queue_master_locator)-> rabbit_queue_master_locator.
137+
class_module(queue_master_locator)-> rabbit_queue_master_locator;
138+
class_module(password_hashing_mod)-> rabbit_password_hashing.
138139

139140
%%---------------------------------------------------------------------------
140141

src/rabbit_upgrade_functions.erl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
-rabbit_upgrade({down_slave_nodes, mnesia, [queue_decorators]}).
5252
-rabbit_upgrade({queue_state, mnesia, [down_slave_nodes]}).
5353
-rabbit_upgrade({recoverable_slaves, mnesia, [queue_state]}).
54+
-rabbit_upgrade({add_hashing_algorithm_to_internal_user, mnesia, [hash_passwords]}).
5455

5556
%% -------------------------------------------------------------------
5657

@@ -84,6 +85,7 @@
8485
-spec(down_slave_nodes/0 :: () -> 'ok').
8586
-spec(queue_state/0 :: () -> 'ok').
8687
-spec(recoverable_slaves/0 :: () -> 'ok').
88+
-spec(add_hashing_algorithm_to_internal_user/0 :: () -> 'ok').
8789

8890
-endif.
8991

@@ -431,6 +433,16 @@ recoverable_slaves(Table) ->
431433
sync_slave_pids, recoverable_slaves, policy, gm_pids, decorators,
432434
state]).
433435

436+
%% Prior to 3.6.0, passwords were hashed using MD5.
437+
%% Users created with 3.6.0+
438+
%% will have internal_user.hashing_algorithm populated.
439+
add_hashing_algorithm_to_internal_user() ->
440+
transform(
441+
rabbit_user,
442+
fun ({user, Username, Hash, IsAdmin}) ->
443+
{user, Username, Hash, IsAdmin, md5}
444+
end,
445+
[username, password_hash, is_admin, hashing_algorithm]).
434446

435447
%%--------------------------------------------------------------------
436448

test/src/rabbit_tests.erl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,9 @@ test_user_management() ->
10151015
TestTags([administrator]),
10161016
TestTags([]),
10171017

1018+
%% hashing functions
1019+
%% TODO
1020+
10181021
%% vhost creation
10191022
ok = control_action(add_vhost, ["/testhost"]),
10201023
{error, {vhost_already_exists, _}} =

0 commit comments

Comments
 (0)