diff options
author | Simon MacMullen <simon@rabbitmq.com> | 2010-11-05 16:39:18 +0000 |
---|---|---|
committer | Simon MacMullen <simon@rabbitmq.com> | 2010-11-05 16:39:18 +0000 |
commit | e50c638f7ea1907ed73687c307584eb5b93d0062 (patch) | |
tree | 672238264b6b88146c782565152b833501174abe | |
parent | 4f83b18f551a30e88f32b264a28f74f910a07c0a (diff) | |
download | rabbitmq-server-e50c638f7ea1907ed73687c307584eb5b93d0062.tar.gz |
Move existing PLAIN and AMQPLAIN auth mechanisms into plugins.
-rw-r--r-- | include/rabbit_auth_mechanism_spec.hrl | 35 | ||||
-rw-r--r-- | src/rabbit_access_control.erl | 85 | ||||
-rw-r--r-- | src/rabbit_auth_mechanism.erl | 53 | ||||
-rw-r--r-- | src/rabbit_auth_mechanism_amqplain.erl | 73 | ||||
-rw-r--r-- | src/rabbit_auth_mechanism_plain.erl | 64 | ||||
-rw-r--r-- | src/rabbit_reader.erl | 11 | ||||
-rw-r--r-- | src/rabbit_registry.erl | 17 |
7 files changed, 283 insertions, 55 deletions
diff --git a/include/rabbit_auth_mechanism_spec.hrl b/include/rabbit_auth_mechanism_spec.hrl new file mode 100644 index 00000000..f88bebb5 --- /dev/null +++ b/include/rabbit_auth_mechanism_spec.hrl @@ -0,0 +1,35 @@ +%% 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): ______________________________________. +%% +-ifdef(use_specs). + +%% TODO + +-endif. diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 15897dfa..62653fbe 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([check_login/2, user_pass_login/2, check_user_pass_login/2, +-export([auth_mechanisms/1, check_login/2, check_user_pass_login/2, 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]). @@ -54,12 +54,10 @@ -type(password() :: binary()). -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(user_pass_login/2 :: - (username(), password()) - -> rabbit_types:user() | rabbit_types:channel_exit()). -spec(check_user_pass_login/2 :: (username(), password()) -> {'ok', rabbit_types:user()} | 'refused'). @@ -99,54 +97,51 @@ %%---------------------------------------------------------------------------- -%% SASL PLAIN, as used by the Qpid Java client and our clients. Also, -%% apparently, by OpenAMQ. -check_login(<<"PLAIN">>, Response) -> - [User, Pass] = [list_to_binary(T) || - T <- string:tokens(binary_to_list(Response), [0])], - user_pass_login(User, Pass); -%% AMQPLAIN, as used by Qpid Python test suite. The 0-8 spec actually -%% defines this as PLAIN, but in 0-9 that definition is gone, instead -%% referring generically to "SASL security mechanism", i.e. the above. -check_login(<<"AMQPLAIN">>, Response) -> - LoginTable = rabbit_binary_parser:parse_table(Response), - case {lists:keysearch(<<"LOGIN">>, 1, LoginTable), - lists:keysearch(<<"PASSWORD">>, 1, LoginTable)} of - {{value, {_, longstr, User}}, - {value, {_, longstr, Pass}}} -> - user_pass_login(User, Pass); - _ -> - %% Is this an information leak? +auth_mechanisms(Sock) -> + Mechanisms = + [atom_to_list(Name) + || {Name, Mechanism} <- rabbit_registry:lookup_all(auth_mechanism), + 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, - "AMQPPLAIN auth info ~w is missing LOGIN or PASSWORD field", - [LoginTable]) - end; - -check_login(Mechanism, _Response) -> - rabbit_misc:protocol_error( - access_refused, "unsupported authentication mechanism '~s'", - [Mechanism]). - -user_pass_login(User, Pass) -> - ?LOGDEBUG("Login with user ~p pass ~p~n", [User, Pass]), - case check_user_pass_login(User, Pass) of - refused -> + 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( - access_refused, "login refused for user '~s'", [User]); - {ok, U} -> - U + 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(User, Pass) -> - case lookup_user(User) of - {ok, U} -> - case check_password(Pass, U#user.password_hash) of - true -> {ok, U}; +check_user_pass_login(Username, Pass) -> + case lookup_user(Username) of + {ok, User} -> + case check_password(Pass, User#user.password_hash) of + true -> {ok, User}; _ -> refused end; {error, not_found} -> - refused + {refused, Username} end. internal_lookup_vhost_access(Username, VHostPath) -> diff --git a/src/rabbit_auth_mechanism.erl b/src/rabbit_auth_mechanism.erl new file mode 100644 index 00000000..2e374320 --- /dev/null +++ b/src/rabbit_auth_mechanism.erl @@ -0,0 +1,53 @@ +%% 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). + +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> + [ + %% A description. Currently unused, may find its way into mgmt one day. + {description, 0}, + + %% If this mechanism is enabled, should it be offered for a given socket? + %% (primarily so EXTERNAL can be SSL-only) + {should_offer, 1}, + + %% Called before authentication starts. Should create a state + %% object to be passed through all the stages of authentication. + {init, 0}, + + %% Handle a stage of authentication + {handle_response, 2} + ]; +behaviour_info(_Other) -> + undefined. diff --git a/src/rabbit_auth_mechanism_amqplain.erl b/src/rabbit_auth_mechanism_amqplain.erl new file mode 100644 index 00000000..61f61e40 --- /dev/null +++ b/src/rabbit_auth_mechanism_amqplain.erl @@ -0,0 +1,73 @@ +%% 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_amqplain). +-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 amqplain"}, + {mfa, {rabbit_registry, register, + [auth_mechanism, <<"AMQPLAIN">>, ?MODULE]}}, + {requires, rabbit_registry}, + {enables, kernel_ready}]}). + +%% AMQPLAIN, as used by Qpid Python test suite. The 0-8 spec actually +%% defines this as PLAIN, but in 0-9 that definition is gone, instead +%% referring generically to "SASL security mechanism", i.e. the above. + +description() -> + [{name, <<"AMQPLAIN">>}, + {description, <<"QPid AMQPLAIN mechanism">>}]. + +should_offer(_Sock) -> + true. + +init() -> + []. + +handle_response(Response, _State) -> + LoginTable = rabbit_binary_parser:parse_table(Response), + case {lists:keysearch(<<"LOGIN">>, 1, LoginTable), + lists:keysearch(<<"PASSWORD">>, 1, LoginTable)} of + {{value, {_, longstr, User}}, + {value, {_, longstr, Pass}}} -> + rabbit_access_control:check_user_pass_login(User, Pass); + _ -> + {protocol_error, + "AMQPLAIN auth info ~w is missing LOGIN or PASSWORD field", + [LoginTable]} + end. diff --git a/src/rabbit_auth_mechanism_plain.erl b/src/rabbit_auth_mechanism_plain.erl new file mode 100644 index 00000000..28aed92b --- /dev/null +++ b/src/rabbit_auth_mechanism_plain.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 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_plain). +-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, <<"PLAIN">>, ?MODULE]}}, + {requires, rabbit_registry}, + {enables, kernel_ready}]}). + +%% SASL PLAIN, as used by the Qpid Java client and our clients. Also, +%% apparently, by OpenAMQ. + +description() -> + [{name, <<"PLAIN">>}, + {description, <<"SASL PLAIN authentication mechanism">>}]. + +should_offer(_Sock) -> + true. + +init() -> + []. + +handle_response(Response, _State) -> + [User, Pass] = [list_to_binary(T) || + T <- string:tokens(binary_to_list(Response), [0])], + rabbit_access_control:check_user_pass_login(User, Pass). diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 127467bb..748b537f 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -691,11 +691,12 @@ handle_input(Callback, Data, _State) -> start_connection({ProtocolMajor, ProtocolMinor, _ProtocolRevision}, Protocol, State = #v1{sock = Sock, connection = Connection}) -> - Start = #'connection.start'{ version_major = ProtocolMajor, - version_minor = ProtocolMinor, - server_properties = server_properties(), - mechanisms = <<"PLAIN AMQPLAIN">>, - locales = <<"en_US">> }, + Start = #'connection.start'{ + version_major = ProtocolMajor, + version_minor = ProtocolMinor, + server_properties = server_properties(), + mechanisms = rabbit_access_control:auth_mechanisms(Sock), + locales = <<"en_US">> }, ok = send_on_channel0(Sock, Start, Protocol), {State#v1{connection = Connection#connection{ timeout_sec = ?NORMAL_TIMEOUT, diff --git a/src/rabbit_registry.erl b/src/rabbit_registry.erl index 227b64f1..8d13f4e1 100644 --- a/src/rabbit_registry.erl +++ b/src/rabbit_registry.erl @@ -38,7 +38,7 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --export([register/3, binary_to_type/1, lookup_module/2]). +-export([register/3, binary_to_type/1, lookup_module/2, lookup_all/1]). -define(SERVER, ?MODULE). -define(ETS_NAME, ?MODULE). @@ -51,6 +51,7 @@ (binary()) -> atom() | rabbit_types:error('not_found')). -spec(lookup_module/2 :: (atom(), atom()) -> rabbit_types:ok_or_error2(atom(), 'not_found')). +-spec(lookup_all/1 :: (atom()) -> [atom()]). -endif. @@ -82,6 +83,9 @@ lookup_module(Class, T) when is_atom(T) -> {error, not_found} end. +lookup_all(Class) -> + [{K, V} || [K, V] <- ets:match(?ETS_NAME, {{Class, '$1'}, '$2'})]. + %%--------------------------------------------------------------------------- internal_binary_to_type(TypeBin) when is_binary(TypeBin) -> @@ -89,23 +93,26 @@ internal_binary_to_type(TypeBin) when is_binary(TypeBin) -> internal_register(Class, TypeName, ModuleName) when is_atom(Class), is_binary(TypeName), is_atom(ModuleName) -> - ok = sanity_check_module(ModuleName), + ok = sanity_check_module(class_module(Class), ModuleName), true = ets:insert(?ETS_NAME, {{Class, internal_binary_to_type(TypeName)}, ModuleName}), ok. -sanity_check_module(Module) -> - case catch lists:member(rabbit_exchange_type, +sanity_check_module(ClassModule, Module) -> + case catch lists:member(ClassModule, lists:flatten( [Bs || {Attr, Bs} <- Module:module_info(attributes), Attr =:= behavior orelse Attr =:= behaviour])) of {'EXIT', {undef, _}} -> {error, not_module}; - false -> {error, not_exchange_type}; + false -> {error, {not_type, ClassModule}}; true -> ok end. +class_module(exchange) -> rabbit_exchange_type; +class_module(auth_mechanism) -> rabbit_auth_mechanism. + %%--------------------------------------------------------------------------- init([]) -> |