summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSimon MacMullen <simon@rabbitmq.com>2010-11-09 16:05:37 +0000
committerSimon MacMullen <simon@rabbitmq.com>2010-11-09 16:05:37 +0000
commit289d7561c848de6002838cd699094955e25c156d (patch)
tree2fdeeaaea397bced6457c7b5e1ac49d57bffbd40 /src
parent97ce437419f70320e12f7e7b318766417d725b14 (diff)
downloadrabbitmq-server-git-289d7561c848de6002838cd699094955e25c156d.tar.gz
Add our slightly flaky demo challenge-response mechanism, and update APIs etc to cope with it.
Diffstat (limited to 'src')
-rw-r--r--src/rabbit_access_control.erl35
-rw-r--r--src/rabbit_auth_mechanism_scram_md5.erl95
-rw-r--r--src/rabbit_reader.erl76
3 files changed, 158 insertions, 48 deletions
diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl
index 62653fbe72..8d4e49e555 100644
--- a/src/rabbit_access_control.erl
+++ b/src/rabbit_access_control.erl
@@ -33,7 +33,7 @@
-include_lib("stdlib/include/qlc.hrl").
-include("rabbit.hrl").
--export([auth_mechanisms/1, check_login/2, check_user_pass_login/2,
+-export([auth_mechanisms/1, check_user_pass_login/2, make_salt/0,
check_vhost_access/2, check_resource_access/3]).
-export([add_user/2, delete_user/1, change_password/2, set_admin/1,
clear_admin/1, list_users/0, lookup_user/1]).
@@ -55,12 +55,10 @@
-type(password_hash() :: binary()).
-type(regexp() :: binary()).
-spec(auth_mechanisms/1 :: (rabbit_networking:socket()) -> binary()).
--spec(check_login/2 ::
- (binary(), binary()) -> rabbit_types:user() |
- rabbit_types:channel_exit()).
-spec(check_user_pass_login/2 ::
(username(), password())
-> {'ok', rabbit_types:user()} | 'refused').
+-spec(make_salt/0 :: () -> binary()).
-spec(check_vhost_access/2 ::
(rabbit_types:user(), rabbit_types:vhost())
-> 'ok' | rabbit_types:channel_exit()).
@@ -104,35 +102,6 @@ auth_mechanisms(Sock) ->
Mechanism:should_offer(Sock)],
list_to_binary(string:join(Mechanisms, " ")).
-check_login(MechanismBin, Response) ->
- Mechanism = mechanism_to_module(MechanismBin),
- State = Mechanism:init(),
- case Mechanism:handle_response(Response, State) of
- {refused, Username} ->
- rabbit_misc:protocol_error(
- access_refused, "login refused for user '~s'", [Username]);
- {protocol_error, Msg, Args} ->
- rabbit_misc:protocol_error(access_refused, Msg, Args);
- {ok, User} ->
- User
- end.
-
-mechanism_to_module(TypeBin) ->
- case rabbit_registry:binary_to_type(TypeBin) of
- {error, not_found} ->
- rabbit_misc:protocol_error(
- command_invalid, "unknown authentication mechanism '~s'",
- [TypeBin]);
- T ->
- case rabbit_registry:lookup_module(auth_mechanism, T) of
- {error, not_found} -> rabbit_misc:protocol_error(
- command_invalid,
- "invalid authentication mechanism '~s'",
- [T]);
- {ok, Module} -> Module
- end
- end.
-
check_user_pass_login(Username, Pass) ->
case lookup_user(Username) of
{ok, User} ->
diff --git a/src/rabbit_auth_mechanism_scram_md5.erl b/src/rabbit_auth_mechanism_scram_md5.erl
new file mode 100644
index 0000000000..f8ec6c4ccc
--- /dev/null
+++ b/src/rabbit_auth_mechanism_scram_md5.erl
@@ -0,0 +1,95 @@
+%% 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 Developers of the Original Code are LShift Ltd,
+%% Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd.
+%%
+%% Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd,
+%% Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd
+%% are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial
+%% Technologies LLC, and Rabbit Technologies Ltd.
+%%
+%% Portions created by LShift Ltd are Copyright (C) 2007-2010 LShift
+%% Ltd. Portions created by Cohesive Financial Technologies LLC are
+%% Copyright (C) 2007-2010 Cohesive Financial Technologies
+%% LLC. Portions created by Rabbit Technologies Ltd are Copyright
+%% (C) 2007-2010 Rabbit Technologies Ltd.
+%%
+%% All Rights Reserved.
+%%
+%% Contributor(s): ______________________________________.
+%%
+
+-module(rabbit_auth_mechanism_scram_md5).
+-include("rabbit.hrl").
+
+-behaviour(rabbit_auth_mechanism).
+
+-export([description/0, should_offer/1, init/0, handle_response/2]).
+
+-include("rabbit_auth_mechanism_spec.hrl").
+
+-rabbit_boot_step({?MODULE,
+ [{description, "auth mechanism plain"},
+ {mfa, {rabbit_registry, register,
+ [auth_mechanism, <<"RABBIT-SCRAM-MD5">>,
+ ?MODULE]}},
+ {requires, rabbit_registry},
+ {enables, kernel_ready}]}).
+
+-record(state, {username = undefined, salt2 = undefined}).
+
+%% START-OK: Username
+%% SECURE: {Salt1, Salt2} (where Salt1 is the salt from the db and
+%% Salt2 differs every time)
+%% SECURE-OK: md5(Salt2 ++ md5(Salt1 ++ Password))
+
+%% The second salt is there to defend against replay attacks. The
+%% first is needed since the passwords are salted in the db.
+
+%% This is only somewhat improved security over PLAIN (if you can
+%% break MD5 you can still replay attack) but it's better than nothing
+%% and mostly there to prove the use of SECURE / SECURE-OK frames.
+
+description() ->
+ [{name, <<"RABBIT-SCRAM-MD5">>},
+ {description, <<"RabbitMQ SCRAM-MD5 authentication mechanism">>}].
+
+should_offer(_Sock) ->
+ true.
+
+init() ->
+ #state{}.
+
+handle_response(Username, State = #state{username = undefined}) ->
+ case rabbit_access_control:lookup_user(Username) of
+ {ok, User} ->
+ <<Salt1:4/binary, _/binary>> = User#user.password_hash,
+ Salt2 = rabbit_access_control:make_salt(),
+ {challenge, <<Salt1/binary, Salt2/binary>>,
+ State#state{username = Username, salt2 = Salt2}};
+ {error, not_found} ->
+ {refused, Username} %% TODO information leak
+ end;
+
+handle_response(Response, #state{username = Username, salt2 = Salt2}) ->
+ case rabbit_access_control:lookup_user(Username) of
+ {ok, User} ->
+ <<_:4/binary, Hash/binary>> = User#user.password_hash,
+ Expected = erlang:md5(<<Salt2/binary, Hash/binary>>),
+ case Response of
+ Expected -> {ok, User};
+ _ -> {refused, Username}
+ end;
+ {error, not_found} ->
+ {refused, Username}
+ end.
diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl
index 748b537ff2..907a00a8ea 100644
--- a/src/rabbit_reader.erl
+++ b/src/rabbit_reader.erl
@@ -60,7 +60,8 @@
-record(v1, {parent, sock, connection, callback, recv_length, recv_ref,
connection_state, queue_collector, heartbeater, stats_timer,
- channel_sup_sup_pid, start_heartbeat_fun}).
+ channel_sup_sup_pid, start_heartbeat_fun, auth_mechanism,
+ auth_state}).
-define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt,
send_pend, state, channels]).
@@ -301,7 +302,9 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb,
stats_timer =
rabbit_event:init_stats_timer(),
channel_sup_sup_pid = ChannelSupSupPid,
- start_heartbeat_fun = StartHeartbeatFun
+ start_heartbeat_fun = StartHeartbeatFun,
+ auth_mechanism = none,
+ auth_state = none
},
handshake, 8))
catch
@@ -744,19 +747,21 @@ handle_method0(MethodName, FieldsBin,
handle_method0(#'connection.start_ok'{mechanism = Mechanism,
response = Response,
client_properties = ClientProperties},
- State = #v1{connection_state = starting,
- connection = Connection =
- #connection{protocol = Protocol},
- sock = Sock}) ->
- User = rabbit_access_control:check_login(Mechanism, Response),
- Tune = #'connection.tune'{channel_max = 0,
- frame_max = ?FRAME_MAX,
- heartbeat = 0},
- ok = send_on_channel0(Sock, Tune, Protocol),
- State#v1{connection_state = tuning,
- connection = Connection#connection{
- user = User,
- client_properties = ClientProperties}};
+ State0 = #v1{connection_state = starting,
+ connection = Connection}) ->
+ AuthMechanism = auth_mechanism_to_module(Mechanism),
+ State = State0#v1{auth_mechanism = AuthMechanism,
+ auth_state = AuthMechanism:init(),
+ connection_state = securing,
+ connection =
+ Connection#connection{
+ client_properties = ClientProperties}},
+ auth_phase(Response, State);
+
+handle_method0(#'connection.secure_ok'{response = Response},
+ State = #v1{connection_state = securing}) ->
+ auth_phase(Response, State);
+
handle_method0(#'connection.tune_ok'{frame_max = FrameMax,
heartbeat = ClientHeartbeat},
State = #v1{connection_state = tuning,
@@ -826,6 +831,47 @@ handle_method0(_Method, #v1{connection_state = S}) ->
send_on_channel0(Sock, Method, Protocol) ->
ok = rabbit_writer:internal_send_command(Sock, 0, Method, Protocol).
+auth_mechanism_to_module(TypeBin) ->
+ case rabbit_registry:binary_to_type(TypeBin) of
+ {error, not_found} ->
+ rabbit_misc:protocol_error(
+ command_invalid, "unknown authentication mechanism '~s'",
+ [TypeBin]);
+ T ->
+ case rabbit_registry:lookup_module(auth_mechanism, T) of
+ {error, not_found} -> rabbit_misc:protocol_error(
+ command_invalid,
+ "invalid authentication mechanism '~s'",
+ [T]);
+ {ok, Module} -> Module
+ end
+ end.
+
+auth_phase(Response,
+ State = #v1{auth_mechanism = AuthMechanism,
+ auth_state = AuthState,
+ connection = Connection =
+ #connection{protocol = Protocol},
+ sock = Sock}) ->
+ case AuthMechanism:handle_response(Response, AuthState) of
+ {refused, Username} ->
+ rabbit_misc:protocol_error(
+ access_refused, "login refused for user '~s'", [Username]);
+ {protocol_error, Msg, Args} ->
+ rabbit_misc:protocol_error(access_refused, Msg, Args);
+ {challenge, Challenge, AuthState1} ->
+ Secure = #'connection.secure'{challenge = Challenge},
+ ok = send_on_channel0(Sock, Secure, Protocol),
+ State#v1{auth_state = AuthState1};
+ {ok, User} ->
+ Tune = #'connection.tune'{channel_max = 0,
+ frame_max = ?FRAME_MAX,
+ heartbeat = 0},
+ ok = send_on_channel0(Sock, Tune, Protocol),
+ State#v1{connection_state = tuning,
+ connection = Connection#connection{user = User}}
+ end.
+
%%--------------------------------------------------------------------------
infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items].