diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/rabbit_auth_backend_internal.erl | 53 | ||||
| -rw-r--r-- | src/rabbit_password.erl | 64 | ||||
| -rw-r--r-- | src/rabbit_password_hashing.erl | 33 | ||||
| -rw-r--r-- | src/rabbit_password_hashing_md5.erl | 28 | ||||
| -rw-r--r-- | src/rabbit_password_hashing_sha256.erl | 24 | ||||
| -rw-r--r-- | src/rabbit_password_hashing_sha512.erl | 24 | ||||
| -rw-r--r-- | src/rabbit_upgrade_functions.erl | 17 |
7 files changed, 217 insertions, 26 deletions
diff --git a/src/rabbit_auth_backend_internal.erl b/src/rabbit_auth_backend_internal.erl index ced109d3ff..eaef7cfda5 100644 --- a/src/rabbit_auth_backend_internal.erl +++ b/src/rabbit_auth_backend_internal.erl @@ -25,7 +25,7 @@ -export([add_user/2, delete_user/1, lookup_user/1, change_password/2, clear_password/1, - hash_password/1, change_password_hash/2, + hash_password/2, change_password_hash/2, set_tags/2, set_permissions/5, clear_permissions/2]). -export([user_info_keys/0, perms_info_keys/0, user_perms_info_keys/0, vhost_perms_info_keys/0, @@ -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). @@ -48,7 +51,7 @@ -spec(change_password/2 :: (rabbit_types:username(), rabbit_types:password()) -> 'ok'). -spec(clear_password/1 :: (rabbit_types:username()) -> 'ok'). --spec(hash_password/1 :: (rabbit_types:password()) +-spec(hash_password/2 :: (module(), rabbit_types:password()) -> rabbit_types:password_hash()). -spec(change_password_hash/2 :: (rabbit_types:username(), rabbit_types:password_hash()) -> 'ok'). @@ -77,13 +80,22 @@ %%---------------------------------------------------------------------------- %% 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 +159,19 @@ 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(HashingMod, 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 @@ -191,7 +205,8 @@ lookup_user(Username) -> change_password(Username, Password) -> rabbit_log:info("Changing password for '~s'~n", [Username]), - R = change_password_hash(Username, hash_password(Password)), + R = change_password_hash(Username, + hash_password(rabbit_password:hashing_mod(), Password)), rabbit_event:notify(user_password_changed, [{name, Username}]), R. @@ -201,14 +216,8 @@ clear_password(Username) -> rabbit_event:notify(user_password_cleared, [{name, 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>>. +hash_password(HashingMod, Cleartext) -> + rabbit_password:hash(HashingMod, Cleartext). change_password_hash(Username, PasswordHash) -> update_user(Username, fun(User) -> @@ -216,10 +225,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..7bc1b28e21 --- /dev/null +++ b/src/rabbit_password.erl @@ -0,0 +1,64 @@ +%% 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, hash/2, generate_salt/0, salted_hash/2, salted_hash/3, + hashing_mod/0, hashing_mod/1]). + +hash(Cleartext) -> + hash(hashing_mod(), Cleartext). + +hash(HashingMod, Cleartext) -> + SaltBin = generate_salt(), + Hash = salted_hash(HashingMod, 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_module, + ?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..3aff2fec4b --- /dev/null +++ b/src/rabbit_password_hashing.erl @@ -0,0 +1,33 @@ +%% 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..7d3e0d80f7 --- /dev/null +++ b/src/rabbit_password_hashing_md5.erl @@ -0,0 +1,28 @@ +%% 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 a 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_password_hashing_sha512.erl b/src/rabbit_password_hashing_sha512.erl new file mode 100644 index 0000000000..50ea22a6d2 --- /dev/null +++ b/src/rabbit_password_hashing_sha512.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_sha512). + +-behaviour(rabbit_password_hashing). + +-export([hash/1]). + +hash(Binary) -> + crypto:hash(sha512, Binary). diff --git a/src/rabbit_upgrade_functions.erl b/src/rabbit_upgrade_functions.erl index 4eced3f32f..f516737fbc 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({user_password_hashing, mnesia, [hash_passwords]}). %% ------------------------------------------------------------------- @@ -84,6 +85,7 @@ -spec(down_slave_nodes/0 :: () -> 'ok'). -spec(queue_state/0 :: () -> 'ok'). -spec(recoverable_slaves/0 :: () -> 'ok'). +-spec(user_password_hashing/0 :: () -> 'ok'). -endif. @@ -431,6 +433,17 @@ recoverable_slaves(Table) -> sync_slave_pids, recoverable_slaves, policy, gm_pids, decorators, state]). +%% Prior to 3.6.0, passwords were hashed using MD5, this populates +%% existing records with said default. Users created with 3.6.0+ will +%% have internal_user.hashing_algorithm populated by the internal +%% authn backend. +user_password_hashing() -> + transform( + rabbit_user, + fun ({internal_user, Username, Hash, Tags}) -> + {internal_user, Username, Hash, Tags, rabbit_password_hashing_md5} + end, + [username, password_hash, tags, hashing_algorithm]). %%-------------------------------------------------------------------- @@ -452,8 +465,8 @@ create(Tab, TabDef) -> %% Dumb replacement for rabbit_exchange:declare that does not require %% the exchange type registry or worker pool to be running by dint of %% not validating anything and assuming the exchange type does not -%% require serialisation. -%% NB: this assumes the pre-exchange-scratch-space format +%% require serialisation. NB: this assumes the +%% pre-exchange-scratch-space format declare_exchange(XName, Type) -> X = {exchange, XName, Type, true, false, false, []}, ok = mnesia:dirty_write(rabbit_durable_exchange, X). |
