diff options
author | Simon MacMullen <simon@rabbitmq.com> | 2014-02-24 14:46:43 +0000 |
---|---|---|
committer | Simon MacMullen <simon@rabbitmq.com> | 2014-02-24 14:46:43 +0000 |
commit | c1ccbf1e0ac7d3014b04a75a5d7954ef2d3554e2 (patch) | |
tree | c20b458920440ac8f270f03ba847ea4ecf2865b2 | |
parent | d053e071af734877847e81445e1b34b36b95cd51 (diff) | |
parent | 7ac4a1820b7564459e182ae90b2b74229dad4296 (diff) | |
download | rabbitmq-server-c1ccbf1e0ac7d3014b04a75a5d7954ef2d3554e2.tar.gz |
Merge in defaultbug25603
-rw-r--r-- | ebin/rabbit_app.in | 1 | ||||
-rw-r--r-- | src/rabbit_access_control.erl | 13 | ||||
-rw-r--r-- | src/rabbit_net.erl | 17 | ||||
-rw-r--r-- | src/rabbit_reader.erl | 47 |
4 files changed, 57 insertions, 21 deletions
diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 29f06e79..7360208a 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -34,6 +34,7 @@ {default_user_tags, [administrator]}, {default_vhost, <<"/">>}, {default_permissions, [<<".*">>, <<".*">>, <<".*">>]}, + {loopback_users, [<<"guest">>]}, {cluster_nodes, {[], disc}}, {server_properties, []}, {collect_statistics, none}, diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 19171659..4bb1aed1 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -18,7 +18,7 @@ -include("rabbit.hrl"). --export([check_user_pass_login/2, check_user_login/2, +-export([check_user_pass_login/2, check_user_login/2, check_user_loopback/2, check_vhost_access/2, check_resource_access/3]). %%---------------------------------------------------------------------------- @@ -35,6 +35,9 @@ -spec(check_user_login/2 :: (rabbit_types:username(), [{atom(), any()}]) -> {'ok', rabbit_types:user()} | {'refused', string(), [any()]}). +-spec(check_user_loopback/2 :: (rabbit_types:username(), + rabbit_net:socket() | inet:ip_address()) + -> 'ok' | 'not_allowed'). -spec(check_vhost_access/2 :: (rabbit_types:user(), rabbit_types:vhost()) -> 'ok' | rabbit_types:channel_exit()). @@ -77,6 +80,14 @@ try_login(Module, Username, AuthProps) -> Else -> Else end. +check_user_loopback(Username, SockOrAddr) -> + {ok, Users} = application:get_env(rabbit, loopback_users), + case rabbit_net:is_loopback(SockOrAddr) + orelse not lists:member(Username, Users) of + true -> ok; + false -> not_allowed + end. + check_vhost_access(User = #user{ username = Username, auth_backend = Module }, VHostPath) -> check_access( diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index 401b8ab1..658474e4 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -20,7 +20,7 @@ -export([is_ssl/1, ssl_info/1, controlling_process/2, getstat/2, recv/1, sync_recv/2, async_recv/3, port_command/2, getopts/2, setopts/2, send/2, close/1, fast_close/1, sockname/1, peername/1, - peercert/1, connection_string/2, socket_ends/2]). + peercert/1, connection_string/2, socket_ends/2, is_loopback/1]). %%--------------------------------------------------------------------------- @@ -77,6 +77,7 @@ (socket(), 'inbound' | 'outbound') -> ok_val_or_error({host_or_ip(), rabbit_networking:ip_port(), host_or_ip(), rabbit_networking:ip_port()})). +-spec(is_loopback/1 :: (socket() | inet:ip_address()) -> boolean()). -endif. @@ -229,3 +230,17 @@ rdns(Addr) -> sock_funs(inbound) -> {fun peername/1, fun sockname/1}; sock_funs(outbound) -> {fun sockname/1, fun peername/1}. + +is_loopback(Sock) when is_port(Sock) ; ?IS_SSL(Sock) -> + case sockname(Sock) of + {ok, {Addr, _Port}} -> is_loopback(Addr); + {error, _} -> false + end; +%% We could parse the results of inet:getifaddrs() instead. But that +%% would be more complex and less maybe Windows-compatible... +is_loopback({127,_,_,_}) -> true; +is_loopback({0,0,0,0,0,0,0,1}) -> true; +is_loopback({0,0,0,0,0,65535,AB,CD}) -> is_loopback(ipv4(AB, CD)); +is_loopback(_) -> false. + +ipv4(AB, CD) -> {AB bsr 8, AB band 255, CD bsr 8, CD band 255}. diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 3304a50b..4a194829 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -1023,29 +1023,12 @@ auth_mechanisms_binary(Sock) -> auth_phase(Response, State = #v1{connection = Connection = #connection{protocol = Protocol, - capabilities = Capabilities, auth_mechanism = {Name, AuthMechanism}, auth_state = AuthState}, sock = Sock}) -> case AuthMechanism:handle_response(Response, AuthState) of {refused, Msg, Args} -> - AmqpError = rabbit_misc:amqp_error( - access_refused, "~s login refused: ~s", - [Name, io_lib:format(Msg, Args)], none), - case rabbit_misc:table_lookup(Capabilities, - <<"authentication_failure_close">>) of - {bool, true} -> - SafeMsg = io_lib:format( - "Login was refused using authentication " - "mechanism ~s. For details see the broker " - "logfile.", [Name]), - AmqpError1 = AmqpError#amqp_error{explanation = SafeMsg}, - {0, CloseMethod} = rabbit_binary_generator:map_exception( - 0, AmqpError1, Protocol), - ok = send_on_channel0(State#v1.sock, CloseMethod, Protocol); - _ -> ok - end, - rabbit_misc:protocol_error(AmqpError); + auth_fail(Msg, Args, Name, State); {protocol_error, Msg, Args} -> rabbit_misc:protocol_error(syntax_error, Msg, Args); {challenge, Challenge, AuthState1} -> @@ -1053,7 +1036,12 @@ auth_phase(Response, ok = send_on_channel0(Sock, Secure, Protocol), State#v1{connection = Connection#connection{ auth_state = AuthState1}}; - {ok, User} -> + {ok, User = #user{username = Username}} -> + case rabbit_access_control:check_user_loopback(Username, Sock) of + ok -> ok; + not_allowed -> auth_fail("user '~s' can only connect via " + "localhost", [Username], Name, State) + end, Tune = #'connection.tune'{frame_max = get_env(frame_max), channel_max = get_env(channel_max), heartbeat = get_env(heartbeat)}, @@ -1063,6 +1051,27 @@ auth_phase(Response, auth_state = none}} end. +auth_fail(Msg, Args, AuthName, + State = #v1{connection = #connection{protocol = Protocol, + capabilities = Capabilities}}) -> + AmqpError = rabbit_misc:amqp_error( + access_refused, "~s login refused: ~s", + [AuthName, io_lib:format(Msg, Args)], none), + case rabbit_misc:table_lookup(Capabilities, + <<"authentication_failure_close">>) of + {bool, true} -> + SafeMsg = io_lib:format( + "Login was refused using authentication " + "mechanism ~s. For details see the broker " + "logfile.", [AuthName]), + AmqpError1 = AmqpError#amqp_error{explanation = SafeMsg}, + {0, CloseMethod} = rabbit_binary_generator:map_exception( + 0, AmqpError1, Protocol), + ok = send_on_channel0(State#v1.sock, CloseMethod, Protocol); + _ -> ok + end, + rabbit_misc:protocol_error(AmqpError). + %%-------------------------------------------------------------------------- infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items]. |