diff options
| author | Michael Klishin <mklishin@pivotal.io> | 2015-09-14 10:15:48 +0300 |
|---|---|---|
| committer | Michael Klishin <mklishin@pivotal.io> | 2015-09-14 10:15:48 +0300 |
| commit | 93b5a3a8092f52063cbca3ab661c7c6bae43c512 (patch) | |
| tree | 29a4333fa05497c2b87fadb824e09e7002a0d5e4 /src | |
| parent | 2eec79191f25368fe60435d5829c028e57426841 (diff) | |
| download | rabbitmq-server-git-93b5a3a8092f52063cbca3ab661c7c6bae43c512.tar.gz | |
Introduce per-user hashing functions, default to SHA-256
References #270.
Diffstat (limited to 'src')
| -rw-r--r-- | src/rabbit_auth_backend_internal.erl | 47 | ||||
| -rw-r--r-- | src/rabbit_password.erl | 61 | ||||
| -rw-r--r-- | src/rabbit_password_hashing.erl | 32 | ||||
| -rw-r--r-- | src/rabbit_password_hashing_md5.erl | 27 | ||||
| -rw-r--r-- | src/rabbit_password_hashing_sha256.erl | 24 | ||||
| -rw-r--r-- | src/rabbit_registry.erl | 3 | ||||
| -rw-r--r-- | src/rabbit_upgrade_functions.erl | 12 |
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]). %%-------------------------------------------------------------------- |
