summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/rabbit_auth_backend_internal.erl47
-rw-r--r--src/rabbit_password.erl61
-rw-r--r--src/rabbit_password_hashing.erl32
-rw-r--r--src/rabbit_password_hashing_md5.erl27
-rw-r--r--src/rabbit_password_hashing_sha256.erl24
-rw-r--r--src/rabbit_registry.erl3
-rw-r--r--src/rabbit_upgrade_functions.erl12
7 files changed, 185 insertions, 21 deletions
diff --git a/src/rabbit_auth_backend_internal.erl b/src/rabbit_auth_backend_internal.erl
index ced109d3ff..c5db74833c 100644
--- a/src/rabbit_auth_backend_internal.erl
+++ b/src/rabbit_auth_backend_internal.erl
@@ -34,6 +34,9 @@
list_user_permissions/1, list_vhost_permissions/1,
list_user_vhost_permissions/2]).
+%% for testing
+-export([hashing_module_for_user/1]).
+
%%----------------------------------------------------------------------------
-ifdef(use_specs).
@@ -77,13 +80,24 @@
%%----------------------------------------------------------------------------
%% Implementation of rabbit_auth_backend
+%% Returns a password hashing module for
+%% the user record provided. If there is no
+%% information in the record, we consider
+%% it to be legacy (inserted by a version older than
+%% 3.6.0) and fall back to MD5, the now obsolete
+%% hashing function.
+hashing_module_for_user(#internal_user{
+ hashing_algorithm = ModOrUndefined}) ->
+ rabbit_password:hashing_mod(ModOrUndefined).
+
user_login_authentication(Username, []) ->
internal_check_user_login(Username, fun(_) -> true end);
user_login_authentication(Username, [{password, Cleartext}]) ->
internal_check_user_login(
Username,
- fun (#internal_user{password_hash = <<Salt:4/binary, Hash/binary>>}) ->
- Hash =:= salted_md5(Salt, Cleartext);
+ fun (#internal_user{password_hash = <<Salt:4/binary, Hash/binary>>} = U) ->
+ Hash =:= rabbit_password:salted_hash(
+ hashing_module_for_user(U), Salt, Cleartext);
(#internal_user{}) ->
false
end);
@@ -147,17 +161,20 @@ permission_index(read) -> #permission.read.
add_user(Username, Password) ->
rabbit_log:info("Creating user '~s'~n", [Username]),
+ %% hash_password will pick the hashing
+ %% function configured for us but we
+ %% also need to store a hint as part of the
+ %% record, so we retrieve it here one more time
+ HashingMod = rabbit_password:hashing_mod(),
+ User = #internal_user{username = Username,
+ password_hash = hash_password(Password),
+ tags = [],
+ hashing_algorithm = HashingMod},
R = rabbit_misc:execute_mnesia_transaction(
fun () ->
case mnesia:wread({rabbit_user, Username}) of
[] ->
- ok = mnesia:write(
- rabbit_user,
- #internal_user{username = Username,
- password_hash =
- hash_password(Password),
- tags = []},
- write);
+ ok = mnesia:write(rabbit_user, User, write);
_ ->
mnesia:abort({user_already_exists, Username})
end
@@ -202,13 +219,7 @@ clear_password(Username) ->
R.
hash_password(Cleartext) ->
- random:seed(erlang:phash2([node()]),
- time_compat:monotonic_time(),
- time_compat:unique_integer()),
- Salt = random:uniform(16#ffffffff),
- SaltBin = <<Salt:32>>,
- Hash = salted_md5(SaltBin, Cleartext),
- <<SaltBin/binary, Hash/binary>>.
+ rabbit_password:hash(Cleartext).
change_password_hash(Username, PasswordHash) ->
update_user(Username, fun(User) ->
@@ -216,10 +227,6 @@ change_password_hash(Username, PasswordHash) ->
password_hash = PasswordHash }
end).
-salted_md5(Salt, Cleartext) ->
- Salted = <<Salt/binary, Cleartext/binary>>,
- erlang:md5(Salted).
-
set_tags(Username, Tags) ->
rabbit_log:info("Setting user tags for user '~s' to ~p~n",
[Username, Tags]),
diff --git a/src/rabbit_password.erl b/src/rabbit_password.erl
new file mode 100644
index 0000000000..79fdffa08b
--- /dev/null
+++ b/src/rabbit_password.erl
@@ -0,0 +1,61 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License
+%% at http://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and
+%% limitations under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
+%%
+-module(rabbit_password).
+-include("rabbit.hrl").
+
+-define(DEFAULT_HASHING_MODULE, rabbit_password_hashing_sha256).
+
+%%
+%% API
+%%
+
+-export([hash/1, generate_salt/0, salted_hash/2, salted_hash/3,
+ hashing_mod/0, hashing_mod/1]).
+
+hash(Cleartext) ->
+ SaltBin = generate_salt(),
+ Hash = salted_hash(SaltBin, Cleartext),
+ <<SaltBin/binary, Hash/binary>>.
+
+generate_salt() ->
+ random:seed(erlang:phash2([node()]),
+ time_compat:monotonic_time(),
+ time_compat:unique_integer()),
+ Salt = random:uniform(16#ffffffff),
+ <<Salt:32>>.
+
+salted_hash(Salt, Cleartext) ->
+ salted_hash(hashing_mod(), Salt, Cleartext).
+
+salted_hash(Mod, Salt, Cleartext) ->
+ Fun = fun Mod:hash/1,
+ Fun(<<Salt/binary, Cleartext/binary>>).
+
+hashing_mod() ->
+ rabbit_misc:get_env(rabbit, password_hashing_mod, ?DEFAULT_HASHING_MODULE).
+
+hashing_mod(rabbit_password_hashing_sha256) ->
+ rabbit_password_hashing_sha256;
+hashing_mod(rabbit_password_hashing_md5) ->
+ rabbit_password_hashing_md5;
+%% fall back to the hashing function that's been
+%% used prior to 3.6.0
+hashing_mod(undefined) ->
+ rabbit_password_hashing_md5;
+%% if a custom module is configured,
+%% simply use it
+hashing_mod(CustomMod) when is_atom(CustomMod) ->
+ CustomMod.
diff --git a/src/rabbit_password_hashing.erl b/src/rabbit_password_hashing.erl
new file mode 100644
index 0000000000..34bb2790c2
--- /dev/null
+++ b/src/rabbit_password_hashing.erl
@@ -0,0 +1,32 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License
+%% at http://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and
+%% limitations under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
+%%
+-module(rabbit_password_hashing).
+-include("rabbit.hrl").
+
+-ifdef(use_specs).
+
+-callback hash(rabbit_types:password()) -> rabbit_types:password_hash().
+
+-else.
+
+-export([behaviour_info/1, ]).
+
+behaviour_info(callbacks) ->
+ [{hash, 1}];
+behaviour_info(_Other) ->
+ undefined.
+
+-endif.
diff --git a/src/rabbit_password_hashing_md5.erl b/src/rabbit_password_hashing_md5.erl
new file mode 100644
index 0000000000..56a0a5ff20
--- /dev/null
+++ b/src/rabbit_password_hashing_md5.erl
@@ -0,0 +1,27 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License
+%% at http://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and
+%% limitations under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
+%%
+
+%% Legacy hashing implementation, only used as the
+%% last resort when #internal_user.hashing_algorithm
+%% is md5 or undefined (the case in pre-3.6.0 user records).
+-module(rabbit_password_hashing_md5).
+
+-behaviour(rabbit_password_hashing).
+
+-export([hash/1]).
+
+hash(Binary) ->
+ erlang:md5(Binary).
diff --git a/src/rabbit_password_hashing_sha256.erl b/src/rabbit_password_hashing_sha256.erl
new file mode 100644
index 0000000000..5d230025b5
--- /dev/null
+++ b/src/rabbit_password_hashing_sha256.erl
@@ -0,0 +1,24 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License
+%% at http://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and
+%% limitations under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
+%%
+
+-module(rabbit_password_hashing_sha256).
+
+-behaviour(rabbit_password_hashing).
+
+-export([hash/1]).
+
+hash(Binary) ->
+ crypto:hash(sha256, Binary).
diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl
index f75d839bbf..291373b7b0 100644
--- a/src/rabbit_registry.erl
+++ b/src/rabbit_registry.erl
@@ -134,7 +134,8 @@ class_module(queue_decorator) -> rabbit_queue_decorator;
class_module(policy_validator) -> rabbit_policy_validator;
class_module(ha_mode) -> rabbit_mirror_queue_mode;
class_module(channel_interceptor) -> rabbit_channel_interceptor;
-class_module(queue_master_locator)-> rabbit_queue_master_locator.
+class_module(queue_master_locator)-> rabbit_queue_master_locator;
+class_module(password_hashing_mod)-> rabbit_password_hashing.
%%---------------------------------------------------------------------------
diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl
index 4eced3f32f..1a08496a14 100644
--- a/src/rabbit_upgrade_functions.erl
+++ b/src/rabbit_upgrade_functions.erl
@@ -51,6 +51,7 @@
-rabbit_upgrade({down_slave_nodes, mnesia, [queue_decorators]}).
-rabbit_upgrade({queue_state, mnesia, [down_slave_nodes]}).
-rabbit_upgrade({recoverable_slaves, mnesia, [queue_state]}).
+-rabbit_upgrade({add_hashing_algorithm_to_internal_user, mnesia, [hash_passwords]}).
%% -------------------------------------------------------------------
@@ -84,6 +85,7 @@
-spec(down_slave_nodes/0 :: () -> 'ok').
-spec(queue_state/0 :: () -> 'ok').
-spec(recoverable_slaves/0 :: () -> 'ok').
+-spec(add_hashing_algorithm_to_internal_user/0 :: () -> 'ok').
-endif.
@@ -431,6 +433,16 @@ recoverable_slaves(Table) ->
sync_slave_pids, recoverable_slaves, policy, gm_pids, decorators,
state]).
+%% Prior to 3.6.0, passwords were hashed using MD5.
+%% Users created with 3.6.0+
+%% will have internal_user.hashing_algorithm populated.
+add_hashing_algorithm_to_internal_user() ->
+ transform(
+ rabbit_user,
+ fun ({user, Username, Hash, IsAdmin}) ->
+ {user, Username, Hash, IsAdmin, md5}
+ end,
+ [username, password_hash, is_admin, hashing_algorithm]).
%%--------------------------------------------------------------------